Correctness of palindrome partition dynamic programming full
This proof shows: correctness.
We prove that PALINDROME-PARTITION-DP returns PL(s), the least number of nonempty palindromes whose concatenation is s=s[0..n−1], together with a partition of s into that many palindromes. Indices are 0-based and s[j..i] includes both ends. We write PLi for PL(s[0..i−1]), so PL0=0.
Lemma 1 (the table). After the run of the outer loop for i, for each 0≤j≤i, the entry pal[j][i] is true if and only if s[j..i] is a palindrome.
Proof. By induction on i. The entry pal[j][i] is set only in the run for i, by line L6, and only if the test of line L5 holds. If i−j≤1, then s[j..i] is a palindrome if and only if s[j]=s[i], which is the test. If i−j≥2, then s[j..i] is a palindrome if and only if s[j]=s[i] and s[j+1..i−1] is a palindrome, because the mirror pairs of s[j..i] are (j,i) and the mirror pairs of s[j+1..i−1]. The entry pal[j+1][i−1] is in column i−1, with j+1≤i−1, so it is correct by the induction hypothesis, and no later run changes it. □
Lemma 2 (the recurrence). For 0≤i≤n−1,
PLi+1=min{PLj+1:0≤j≤i, s[j..i] is a palindrome}. Proof. The set is not empty, because s[i..i] is a palindrome. Let s[0..i]=p1⋯pk be a partition into k=PLi+1 palindromes, and let pk=s[j..i]. Then p1⋯pk−1 is a partition of s[0..j−1] into palindromes, so k−1≥PLj, and the right side is at most k. Conversely, for each j in the set, a partition of s[0..j−1] into PLj palindromes followed by the piece s[j..i] is a partition of s[0..i] into PLj+1 palindromes, so PLi+1≤PLj+1. □
Lemma 3 (the values). After the run of the outer loop for i, best[i+1]=PLi+1, and s[0..start[i+1]−1] followed by s[start[i+1]..i] is a split whose last piece is a palindrome and with best[start[i+1]]+1=best[i+1].
Proof. By induction on i, with best[0]=0=PL0 from line L1. In the run for i, line L3 sets best[i+1] to ∞, and the inner loop goes over j=0,…,i. By Lemma 1, the test of line L5 holds exactly for the j with s[j..i] a palindrome, and by the induction hypothesis best[j]=PLj for these j≤i. Line L7 keeps the running minimum of best[j]+1 over them and the j where it was reached. At the end of the run, by Lemma 2, best[i+1]=PLi+1, and start[i+1] is a j with s[j..i] a palindrome and PLj+1=PLi+1. Later runs do not change best[i+1] or start[i+1]. □
Lemma 4 (the rebuild). Lines L8 and L9 put best[n] pieces into pieces, each a palindrome, and their concatenation is s.
Proof. We show the invariant: at each test of line L9, pieces is a list of best[n]−best[i] palindromes whose concatenation is s[i..n−1]. At the start i=n and the list is empty. If i>0, let j=start[i]. By Lemma 3, s[j..i−1] is a palindrome and best[j]=best[i]−1, so after line L9 the list holds best[n]−best[j] palindromes whose concatenation is s[j..n−1], and the new value of i is j. Since j≤i−1, the loop stops after at most n steps, with i=0 and best[0]=0. □
Theorem. PALINDROME-PARTITION-DP returns PL(s) and a partition of s into PL(s) palindromes.
Proof. For n=0 the outer loop does not run and the result is 0 with the empty partition, which is correct. For n≥1, Lemma 3 with i=n−1 gives best[n]=PLn=PL(s), and Lemma 4 shows that the returned pieces form a partition of s into best[n] palindromes. Both loops are bounded, so the algorithm stops. ■