← All problems

Levko And Strings

Codeforces · Round #210 (Div. 1) · Problem C

Problem

We are given a string SS of length N2,000N \leq 2,000. Let TT be any string of length NN. The beauty of TT relative to SS is the number of pairs (i,j)(i,j) so that 1ijN1 \leq i \leq j \leq N and T[i..j]>S[i..j]T[i..j] > S[i..j], where >> means lexicographically. That is, the beauty of a string is the number of substrings which compare greater than the corresponding substring of SS.

Given SS and an integer KK, how many strings are there with a beauty equal to KK, relative to the string SS?

Initial Observations
  1. Substrings
  2. String matching / suffix arrays?
  3. DP
  4. Small NN,KK, maybe O(NK)O(NK) solution
  5. Counting
IdeaIntuition for DPNO
From Experience

The constraints and my experiences seem to imply some kind of Dynamic Programming approach where I can count the strings based on smaller strings. The table/function will probably look like dp[n][k]dp[n][k]. I'm not sure, yet, what the dimensions or the values would actually mean though.

Write It Out

At this point, I decided to follow my intuition about the dp. I couldn't easily describe the dimensions or how the sub-problems would actually be combined. Furthermore, any ideas I did have (at least mentally) for the dp seemed like they will be O(N2K)O(N^2K) in the end. Instead of getting stuck here though, I decided to fully formulate a dynamic programming solution (i.e.: write out the function and what the sub-problems actually are). I was hoping that, once I got a correct answer, that I could optimize the solution later to make sure it ran efficiently.

Lesson

Sometimes it is easy to come up with a dynamic programming solution (or other kind of solution) that is correct but asymptotically too slow, for a given problem. Many people, myself included, will often get stuck thinking "Well there's no point in implementing this DP solution because it is definitely too slow". Usually, this "too slow" comes from a single extra (often redundant) dimension in your state-space. For example, maybe you have dp[a][b][c], but it turns out (for whatever reason) that the only valid "c" values always satisfy the equation: c := a+b+3 Then you do not need the extra dimension. And you can reduce the state-space and (likely) the overall complexity of your solution. Other times, maybe the state-space is correctly characterized (i.e.: no redundant dimensions), but the transitions are costly in some way. For instance, instead of thinking of dp[n] as the sum of values from 1..n (which would require combining n things to compute it), we might write: dp[n] = (n'th value) + dp[n-1]. That would (implicitly) sum over all values (i.e.: by induction, if dp[n-1] is also the sum of values from 1..n-1). In this case, we did not reuse the over-lapping sub-problems nicely, and a minor adjustment could fix it. In all cases, I think it's best to simply write down the mathematical formula for the state-space and the dp. I also think it's wise to also write out PRECISELY what the dp means, eg: dp[n][k] = number of string of length n that have beauty exactly k when compared against S[1..n]. This is precise, and can be verified.

Once all of this is written down, and the formulas are derived we can often find the key observation necessary to make the dp "fast" simply by inspecting the formula. For example, we might see somewhere in the formula, a big "sum". Then we can look at ways of turning this "big sum" into (maybe) a mini-dp that may take only one-step per state to compute. Or we might find a lot of over-lapping sub-problems.

In any case, start by formulating whatever dp comes to mind intuitively. Then, once it is fully formulated, modify and "bash" it, until it is efficient or clean enough to work. If I had done this here, I would have solved the problem a lot sooner.

The dp I finally came up with, was based on the following observations.

Work Backwards

Let's suppose we have a TT. What properties of TT do we need to guarantee in order to ensure that it has a beauty of exactly KK.

Key Question

Can we characterize how all valid TT would look?

Key Observation

Suppose we have some string TT with beauty kk. Then there are kk sub-strings which compare greater than their respective substring in SS. Each such substring looks as follows: It compares equal on a certain number of characters; then there is some character which is greater; then the remaining characters can be anything. Formally, for a pair of indices (i,j)(i,j) contributing to the beauty: there exists an index xx so that ixji \leq x \leq j, T[i..x1]=S[i..x1]T[i..x-1] = S[i..x-1], and T[x]>S[x]T[x] > S[x] and the characters T[x+1..j]T[x+1..j] have no specific relation to S[x+1..j]S[x+1..j]. In some sense, it is that single character T[x]T[x] being greater than S[x]S[x] that characterizes the substring and the pair (i,j)(i,j). So, we observe that we should focus on particular occurrences of characters that compare greater

Process

Given a set of elements which may be "useful" or "good" (whatever that means in a given context), it is often a good idea to look at the "extreme" elements. For example, if the set is a set of numbers, look at the minimum element; or if (in this case), the set of "useful" objects is a set of characters (i.e.: we are concerned with the characters that compare greater), look at the first one (i.e.: one with minimal index).

The above key observation and problem solving process lead to a possible characterization of the dp states and transitions between them. They would be based on the concept of picking a first "differing" character, and then recursively using the results of other dp states.

This solution is described further below. The above information should be enough to provide an intuition for how to solve this problem.

IdeaDP FormulationTLE

Note: I had a couple failed attempts at a solution before arriving at a final dp.

Recall (from the last observations and problem solving processes in the idea above), that we want to characterize our dp solution strings (TT) based on their first character that is bigger than the corresponding character in SS. More generally, let's characterize them by the first character that differs from the corresponding character in SS. That is, it may be bigger or smaller, and we might have to handle these cases differently. This leads to a definition (and characterization).

Definition.

For any string TT, we say a "block" in TT is any substring T[i..j]T[i..j] so that T[i..j1]=S[i..j1]T[i..j-1] = S[i..j-1] but T[j]S[j]T[j] \neq S[j]. A "big block" is any such T[i..j]T[i..j] where T[j]>S[j]T[j] > S[j]. And a "small block" is any such T[i..j]T[i..j] where T[j]<S[j]T[j] < S[j].

Lemma.

Every string TST \neq S can be uniquely decomposed into a sequence of (non-overlapping) blocks.

Proof.

Let j1j_1 be the index of the first character that differs (i.e.: T[j1]S[j1]T[j_1] \neq S[j_1]). Then all characters T[1..j11]=S[1..j11]T[1..j_1-1] = S[1..j_1-1] since T[j1]T[j_1] was the first character to differ. Thus, T[1..j1]T[1..j_1] is a block. And then we decompose T[j1+1..n]T[j_1+1..n] similarly (I guess it would be by induction or by repetition), and we have a sequence of blocks, as desired.

Now we attempt to formulate the dp, using the block decomposition. I write it out as a recursive function.

Definition.

Let T(n,k)T(n,k) be a function which counts the number of strings that have a beauty of kk when matched against S[n..N]S[n..N].

Observation.

If we can somehow compute T(n,k)T(n,k) correctly for all 1nN1 \leq n \leq N and 0kK0 \leq k \leq K, then we're done, and the answer would be T(1,K)T(1,K).

We now compute T(n,k)T(n,k) based on the block decomposition as described above. I work through the formulation step-by-step.

Key Observation

Every substring counted by T(n,k)T(n,k) will either compare exactly equal to S[n..N]S[n..N] or will have at least one differing character. If it has no differing characters, it necessarily has 0 beauty. And if it has at least one differing character, we consider the first differing character, which can occur in any position n,n+1,n+2,...,Nn, n+1, n+2,..., N. So we get that:

T(n,k) = \sum_{x=n}^{N}{\left(\text{# of strings with beauty k and which have first differing character at x}\right)} + \left\{1 \text{ if } k==0\right\}

where the last "+ 1" part comes from the single string that has no differing characters (namely, if T=S[n..N]T=S[n..N] itself). So, besides the "+1", we will focus on computing the main sum directly (with the differing characters).

Key Observation

So, how many strings have beauty kk and a first differing character (among n..Nn..N) in a particular position xx? (We use counting / combinatorics here.)

The characters n..x1n..x-1 must all be equal to the corresponding characters in SS. So there is only one choice for these. Then, character xx will either compare greater or smaller. And we count/handle these cases separately.

If T[x]>S[x]T[x] > S[x], then there are (zS[x])(`z`-S[x]) choices (using ASCII subtraction; i.e.: zz=0`z`-`z` = 0, and zy=1`z`-`y` = 1, etc.) for character T[x]T[x]. The remaining characters T[x+1..N]T[x+1..N] can be anything, as long as the total beauty ends up being kk. How much beauty do we get from using this first block? Well, any substring T[i..j]T[i..j] that has ixi \leq x and jxj \geq x will contribute to the beauty (because it will start with some equal characters, and then compare greater exactly at position xx, so it will be greater overall). And since we are considering only the indices n..Nn..N we get exactly (xn+1)(x-n+1) choices for ii, and (Nx+1)(N-x+1) choices for jj. So, this block contributes exactly: (Nx+1)(xn+1)(N-x+1)*(x-n+1) to the beauty.

And notice, all other indices (i,j)(i,j) that contribute to the beauty must have that i>xi>x strictly. So we could use T(x+1,k(Nx+1)(xn+1))T(x+1,k-(N-x+1)*(x-n+1)) to count the number of ways to fix the remaining characters T[x+1..N]T[x+1..N] to fill up the remaining beauty.

So, in total the number of strings which have a beauty kk among the indices n..Nn..N with a first differing character T[x]>S[x]T[x] > S[x] for (xn)(x \geq n) is exactly: (zS[x])T(x+1,k(Nx+1)(xn+1))(`z`-S[x])*T(x+1, k-(N-x+1)*(x-n+1)).

Similarly, if T[x]<S[x]T[x] < S[x] then there are (S[x]a)(S[x] - `a`) choices for T[x]T[x], this first block adds 0 beauty, and we must choose the remaining characters in T[x+1..N]T[x+1..N] to contribute exactly kk beauty. So, there are exactly: (S[x]a)T(x+1,k)(S[x]-`a`)*T(x+1,k) strings which have a beauty kk among the indices n..Nn..N with a first differing character T[x]<S[x]T[x] < S[x] for (xn)(x \geq n).

Thus, for T(n,k)T(n,k) we get the following formula.

Lemma.

For 1nN1 \leq n \leq N,

T(n,k)=x=nN(zS[x])T(x+1,k(Nx+1)(xn+1))+(S[x]a)T(x+1,k)+{1 if k==0}T(n,k) = \sum_{x=n}^{N}{(`z`-S[x])T(x+1, k-(N-x+1)*(x-n+1)) + (S[x]-`a`)T(x+1,k)} + \left\{1 \text{ if } k==0\right\}

Proof.

This formula comes directly from the key observations above. If xnx \geq n is the index of the first differing character, then either T[x]>S[x]T[x] > S[x] or T[x]<S[x]T[x] < S[x]. In the first case, we get (zS[x])(`z`-S[x]) choices and (Nx+1)(xn+1)(N-x+1)*(x-n+1) beauty, and then recur T(x+1,k(Nx+1)(xn+1))T(x+1, k-(N-x+1)*(x-n+1)). In the second case, we have (S[x]a)(S[x]-`a`) choices and 0 beauty, and then we recur on T(x+1,k)T(x+1,k). The "+1" term comes from the string T=ST=S which has 0 beauty and has no differing character xx.

Hence, we now have a (recursive) formula to compute the answer to our question (which would be T(1,K)T(1,K)). Also, the base case(s) for the above formula is/are: T(n,k)=0T(n,k) = 0 if n<0n<0 or k<0k<0, also T(N+1,0)=1T(N+1,0) = 1 (the empty string), and T(N+1,k)=0T(N+1,k) = 0 for all k1k \geq 1.

Key Question

So we have a correct formulation for T(n,k)T(n,k), which can be solved recursively. What is the total running time needed to compute T(1,K)T(1,K) if we use dynamic programming to memoize the states (i.e.: if we don't ever have to recompute a state twice)?

Lemma.

If carried out directly, the running time of the computation is Θ(N2K)\Theta(N^2K).

Proof.

There are Θ(N×K)\Theta(N \times K) states (possible inputs to the T(.,.)T(.,.) function). For each state, (n,k)(n,k), it takes at worst Θ(Nn)=Θ(N)\Theta(N-n) = \Theta(N) time to compute the sum for all xx as described in the formula. So, in the end, it takes Θ(N2K)\Theta(N^2K) time.

This is too slow. However, I would say that this is 90% of the solution. I will describe in the next "idea" how to optimize this dynamic programming approach to run in time. The reader is encouraged to try getting this above solution to work first, before reading the next section (and you may end up coming up with the better / faster solution anyway).

IdeaOptimizing the DPAC

NOTE: The bulk of the solution was described in Idea 2 above. The reader should read that first. This section merely describes how to improve the DP solution to run within the time-limits. Otherwise, the reader can also simply read the "Review" section below, to see the final solution.

Key Observation

Recall (from the lemma in Idea 2 above), that for 1nN1 \leq n \leq N, T(n,k)=x=nN(zS[x])T(x+1,k(Nx+1)(xn+1))+(S[x]a)T(x+1,k)+{1 if k==0}T(n,k) = \sum_{x=n}^{N}{(`z`-S[x])T(x+1, k-(N-x+1)*(x-n+1)) + (S[x]-`a`)T(x+1,k)} + \left\{1 \text{ if } k==0\right\}.

For the first term, k(Nx+1)(xn+1)k - (N-x+1)*(x-n+1) is a parabola which shrinks rapidly for each xx, before coming back up again. And if you look at the numbers (either by simulation or just intuitively), for most xx, k(Nx+1)(xn+1)<0k - (N-x+1)*(x-n+1) < 0. So, we are just adding up a lot of zeroes.

Key Question

Is there any way to re-order the sum so that we can avoid doing extra work (i.e.: adding up a lot of 0's)?

From Experience

We somehow want to "amortize" the costs of the work done. That is, for a fixed state (n,k)(n,k), there may be O(N)O(N) work done; but we want to show that the total sum of all work done is relatively small, such as O(NK)O(NK) rather than Θ(N2K)\Theta(N^2K).

Work Backwards

I want to transform/rearrange the sum to ensure that I am not adding lots of zeroes and hopefully find a better bound on the amount of work I have to do. For this, instead of asking: "for a fixed (n,k)(n,k), how much work must be done", I will ask "for a specific xx, which states (n,k)(n,k) will receive a non-zero contribution from xx in the sum when computing T(n,k)T(n,k)?" Specifically, I am looking at the first part of the sum, in the formula for T(x+1,k(Nx+1)(xn+1))T(x+1,k - (N-x+1)*(x-n+1)) which I think is the major part that can be optimized.

So, when is k(Nx+1)(xn+1)0k - (N-x+1)*(x-n+1) \geq 0? (I.e.: whenever it is smaller than 0, the answer is obviously zero, so there is no need to count it)

By rearranging the inequality, k(Nx+1)(xn+1)0k - (N-x+1)*(x-n+1) \geq 0 if and only if k(Nx+1)(xn+1)k \geq (N-x+1)*(x-n+1) if and only if kNx+11(xn)\frac{k}{N-x+1} - 1 \geq (x-n).

The right hand side is the "difference" between xx and nn. So, for a fixed xx, the nn to which it might contribute a positive value would be those that are no more than kNx+11\frac{k}{N-x+1} - 1 away. For example, when x=Nx=N, it will contribute to kNN+11=k1\frac{k}{N-N+1} - 1 = k-1 different nn values; when x=N1x=N-1 it will contribute to k21\frac{k}{2}-1 different nn's; when x=N2x=N-2, it will contribute to k31\frac{k}{3}-1 of them, etc. So, all the xx contribute a total of O(k+k2+k3++kN)=O(klog(N))O(k + \frac{k}{2} + \frac{k}{3} + \cdots + \frac{k}{N}) = O(k\log(N)) work in total for any fixed kk. The log(N)\log(N) part comes from the fact that 1+12+13+...+1N=HN1 + \frac{1}{2} + \frac{1}{3} + ... + \frac{1}{N} = H_N is the NthN^{th} "Harmonic Number", which can be approximated by Θ(log(N))\Theta(\log(N)).

This would mean, in an amortized fashion, the total amount of work needed to be done (assuming we only added up those xx which contributed non-zero values to the sum for specific (n,k)(n,k)), would be k=0KO(klog(N))=O(K2log(N))\sum_{k=0}^{K}{O(k\log(N))} = O(K^2\log(N)), which would run in time.

Obviously, I have skipped over a lot of details on HOW that must be done. Also, I have not accounted for "all" of the sum. There is still a T(x+1,k)T(x+1,k) term that needs to be amortized as well. This is much easier, because we can simply keep some kind of running sum of all T(x,k)T(x,k) values, and just add that as well to the sum.

Algorithm

The key is to "work backwards" as described above in the problem solving process. Instead of computing the formula directly for each (n,k)(n,k), keep a table dp[n][k]dp[n][k] which stores T(n,k)T(n,k) (and it will be filled up partially). Initialize it to 0. And work backwards, for each pair (x,k)(x,k). For a fixed (x,k)(x,k) assuming that dp[x][k]dp[x][k] is known, we only update the table entries dp[n][k]dp[n][k] (n<xn < x) for which the index xx actually contributes (i.e.: only those for which k(Nx+1)(xn+1)k - (N-x+1)*(x-n+1) is positive). You also need to keep a running sum sum[k]sum[k] and add dp[x][k]dp[x][k] to that once it is known as well (this deals with the T(x+1,k)T(x+1,k) term in the formula). And as long as you are decreasing xx on each iteration, we will always know dp[x][k]dp[x][k] by the time it is needed, and we can use it to update the dp[n][k]dp[n][k] for smaller nn, and keep going. (When I say "update", I mean, add the corresponding term to the sum, based on the formula described above for T(n,k)T(n,k).)

For clarity, here is the pseudo-ish code.

Let S[1..N] be the given string.
Let dp[n][k] be a 2D-array, initialized all to 0.
Let sum[n][k] be a 2D-array, initialized all to 0.
dp[N+1][0] = 1
sum[N+1][0] = 0   // sum[n][k] := sum { (S[x]-'a')*dp[x+1][k]  :  x = n..N }
 
// T(n,k) = sum_{x=n}^{N}{(`z`-S[x])T(x+1, k-(N-x+1)*(x-n+1)) + (S[x]-`a`)T(x+1,k)} + {1 if k==0}
// For each (x,k), we assume dp[x][k] and sum[x][k] are already computed.
// Then we update dp[n][k] for all relevant n<x and sum[x-1][k]
for x = N+1 downto 2:
  x2 = x - 1   // for convenience (since we are updating x-1, not x; we have to substitute this in the formula above)
  for k = 0 to K:
    sum[x2][k] += (S[x2]-'a')*dp[x2+1][k]
    for n = x2 downto 1:
      if (k - (N-x2+1)*(x2-n+1)) <= K:
        dp[n][k+(N-x2+1)*(x2-n+1)] += ('z'-S[x2])*dp[x2+1][k]
      else:
        break // (x2-n) <= k/(N-x2+1) - 1
return dp[1][K]

I used x2:=x1x_2 := x-1 in the above formula, because we were updating all n<xn < x, but not xx itself. So I found it easier to replace xx with x1x-1 in the formulas. Everything else remains the same though.

This solution works in O(NK)+O(K2log(N))O(NK) + O(K^2\log(N)) time by the earlier analysis.

Review

Summary

This problem is a fairly hard example of Dynamic Programming. It requires not only coming up with a recursive function to count the number of strings, but it also requires computing this function "Bottom-up" (in an iterative fashion) rather than "Top-down" (in a purely-recursive fashion), because we need to exploit the structure of the formula in order to make it run in time.

The dp can be described as follows: Let dp[n][k]dp[n][k] be the number of strings which have beauty kk when compared against S[n..N]S[n..N] (This is the same as the recursive function T(n,k)T(n,k) that I use above in Ideas 1 - 3). Each string with beauty kk must have a "first differing character" in some position xx (assuming k0k \neq 0). All characters before that must compare exactly equal to the corresponding characters of SS. If this "differing character" compares greater (i.e.: T[x]>S[x]T[x] > S[x]), then there are (zS[x])('z'-S[x]) ways to choose this character, and it adds exactly (xn+1)(Nx+1)(x-n+1)*(N-x+1) units to the beauty (i.e.: any substring T[i..j]T[i..j] with nixjNn \leq i \leq x \leq j \leq N will compare "greater" than the corresponding substring S[i..j]S[i..j], so we have exactly (xn+1)(x-n+1) choices for ii and (Nx+1)(N-x+1) choices for jj). So, then we must choose the remaining characters (T[x+1..N]T[x+1..N]) to have exactly k(xn+1)(Nx+1)k - (x-n+1)*(N-x+1) beauty, which is an equivalent sub-problem (it is equal to dp[x+1][k - (x-n+1)*(N-x+1)]).

A similar formula holds if the first differing character compares smaller than the corresponding character in SS, so that T[x]<S[x]T[x] < S[x]. So, we come to the formula:

dp[n][k]=sum(zS[x])dp[x+1][k(xn+1)(Nx+1)]+(S[x]a)dp[x+1][k]):x=n..N+(1ifk==0)dp[n][k] = sum {('z'-S[x])*dp[x+1][k - (x-n+1)*(N-x+1)] + (S[x]-'a')*dp[x+1][k]) : x = n..N} + (1 if k==0)

where the last "+1" comes from the fact that T[n..N]=S[n..N]T[n..N] = S[n..N] might be a possibility (in which case it has no differing character).

By inspection, this dp formula takes Θ(N2K)\Theta(N^2K) time to compute, which would yield Time Limit Exceeded. However, we notice that for a given xx, the term k(xn+1)(Nx+1)<0k - (x-n+1)*(N-x+1) < 0 whenever nn is really small compared to xx. So we work backwards, and once dp[n][k]dp[n][k] has been computed, we treat it as dp[x][k]dp[x][k], and use it to update all dp[n][k]dp[n'][k] with n<xn' < x (based on the dp formula), and break early once nn' becomes too small. We handling the other parts of the sum similarly.

It can be proven that, if computed this way, the overall running time would be Θ(NK+K2log(N))\Theta(NK + K^2\log(N)) which is Accepted.

  1. For a pair of indices (i,j)(i,j) contributing to the beauty: there exists an index xx so that ixji \leq x \leq j, T[i..x1]=S[i..x1]T[i..x-1]=S[i..x-1] , and T[x]>S[x]T[x]>S[x] and the characters T[x+1..j]T[x+1..j] have no specific relation to S[x+1..j]S[x+1..j]. This is the most intuitive observation, but it is also the key to characterizing the solution, if understood correctly.

  2. Every string TST\neq S can be uniquely decomposed into a sequence of (non-overlapping) blocks, where a block is a substring T[i..x]T[i..x] with T[i..x1]=S[i..x1]T[i..x-1] = S[i..x-1] and T[x]S[x]T[x] \neq S[x]. Either T[x]>S[x]T[x] > S[x] or T[x]<S[x]T[x] < S[x]. This key observation provided the recursive structure necessary to construct the dynamic programming solution. We recur based on the start of the current "block".

  3. For 1nN1 \leq n \leq N, T(n,k)=x=nN(zS[x])T(x+1,k(Nx+1)(xn+1))+(S[x]a)T(x+1,k)+{1 if k==0}T(n,k) = \sum_{x=n}^{N}{(`z`-S[x])T(x+1, k-(N-x+1)*(x-n+1)) + (S[x]-`a`)T(x+1,k)} + \left\{1 \text{ if } k==0\right\}. This was the key formula (the DP). It was a result of careful combinatorial arguments, and a result of the recursive structure described above.

  4. The function k(Nx+1)(xn+1)k-(N-x+1)*(x-n+1) is a parabola as a function of xx (for a fixed (n,k)(n,k)), and it often goes below zero. For a fixed (x,k)(x,k), it will go to zero whenever the difference (xn)(x-n) is greater than kNx+1\frac{k}{N-x+1}. So, if we skip all (n,k,x)(n,k,x) so that k(Nx+1)(xn+1)k-(N-x+1)*(x-n+1) is under zero, we don't have to do as much work. This was important to amortize the running time of the algorithm.

  5. This problem looks like a Dynamic Programming problem; maybe I can dp digit by digit. The dp will probably look something like dp[n][k]dp[n][k].

  6. Most of the immediate DP ideas that came to my mind seemed too slow. However, instead of getting caught up on these details, I decided to focus on deriving at least one correct DP formulation, regardless of how inefficient it was. This was crucial. I would not have been able to solve the problem otherwise, because there was no "better DP formula". In the end, I used the same formula, but I just changed the way it was computed to make the running time better.

  7. To determine the formula, I thought "suppose we have a string TT with beauty kk. Then how would it look? What properties would it have?" This was a natural set of questions to ask. And it helped characterize the solution.

  8. "Pursuing Extreme Values" is a common problem-solving technique. "If you have to walk to the store, you will always take a 'first' step." Similarly, if there are blocks in the string, we focused on the first block. The rest could be handled by recursion.

  9. In order to optimize the DP, I knew that I would need to use some kind of "amortized" analysis, which is a concept I've learned from experience (e.g.: See Thomas Cormen's "Introduction to Algorithms")

  10. The dp formula was written as a function T(n,k)T(n,k), but it relied on summing over values of T(x+1,k)T(x+1,k) for certain xnx \geq n. Instead of asking: "How can I compute T(n,k)T(n,k) from the T(x+1,k)T(x+1,k) values", I asked "Given some values for T(x+1,k)T(x+1,k), how does it contribute to various T(n,k)T(n,k) sums?". This "backward" transformation, and the order of computation, became crucial in optimizing the dp so that it would run efficiently. The reason for this was, given (n,k)(n,k) the set of xx that actually contribute a non-zero value to the sum T(n,k)T(n,k) was parabolic (there was a bunch of xx that did contribute, then some that didn't contribute, and then some more that did again). However, for a fixed xx and kk, the set of nn whose sum T(x+1,k)T(x+1,k) contributed to was linear. That is, there is a computable, contiguous, range of nn such that T(n,k)T(n,k) relied on xx. This made it easier to "stop" early, when updating the sums, and also brought down the entire amortized running time to O(K2log(N))O(K^2\log(N)) instead of O(KN2)O(KN^2). This was a crucial application of the problem-solving process.

Related Problems

UVa 10154 - Weights and Measures. This is an unrelated problem (by content), but it is indeed a hard DP problem that requires thinking outside the box and transforming the problem. UVa 1579 - Matryoshka. This is not really related. In fact, I don't even know the solution to this problem. I was thinking about another problem titled "Matryoshka Dolls" from some contest, which was equivalent to the Weights and Measures problem I posted just above; and I happened to come across this problem here, which is a completely different problem with a similar name. I still include it in this list, because, at first glance, maybe it is a DP problem anyway? And I might as well try to solve it. What if the beautiful substrings were those that compared "less"? Then the problem would be symmetric. What if the beautiful substrings were those that compared "different" than SS? Then the problem would be a bit easier, but would still require the DP state optimizations (I think). It would be more regular, and the formula would be similar, except it would be: T(n,k)=x=nN25T(x+1,k(xn+1)(Nx+1))T(n,k) = \sum_{x=n}^{N}{25*T(x+1,k-(x-n+1)(N-x+1))} (or something similar), because the xthx^{th} character need only differ (so there are 25 choices for the character), and all such substrings contribute to the beauty. What if the beautiful substrings were those that compared "equal" to the substrings of SS? This problem would be different, a bit more regular (and easier to compute), but would use the same principle: Pick the first "differing" character. But this time, the beautiful substrings would be those that did not use it, so there would be a formula like: T(n,k)=x=nN25T(x+1,k(xn)2)T(n,k) = \sum_{x=n}^{N}{25*T(x+1,k-(x-n)^2)} (or something similar). Always though, we would need to optimize the state (I think), although this problem is much more regular than the others, it seems).