注意:凡是可以用O(n^2)算法求回文串时候,都可以用dp求解。dp[i][j] = (s[i] == s[j]) ? dp[i + 1][j -1] : 0(即当前两端是否相等,若相等,则只取决于中间是不是回文串)。预处理的时候,只需要令所有的dp[i][i] = 1,dp[i][i - 1] = 1(方便转移)即可。
题解:
Observation I.
If the string is k-palindrome, then it is (k - 1)-palindrome.
Observation II.
The string is k-palindrome if and only if the both following conditions are true:
- It is a palindrome.
- It's left half is non-empty (k - 1)-palindrome.
Solution.
Let's calculate the following dp.
- dp[l][r] is the maximum k such that the substring built from characters from l to r is k-palindrome.
- The dynamics is calculated in the order of non-decreasing of substring lengths.
- The values for l = r and l = r - 1 are computed trivially.
- Let r - l > 1. Then, if s[l] ≠ s[r] or dp[l + 1][r - 1] = 0, dp[l][r] = 0. Otherwise dp[l][r] = dp[l][m] + 1, where
.
When we have dp values, we can calculate cnt[k] — the number of substrings, which dp value is k. Then the number of substrings that are k-palindromes is .
The solution works in O(|s|2) time and uses O(|s|2) memory.
Also, you could notice that the string can be no more than -palindrome, and solve the problem in
time, reducing the memory usage to O(|s|).
Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5000 + 10;
int dp[maxn][maxn];
char s[maxn];
int cnt[maxn];
int lens;
int main()
{
scanf("%s",s);lens = strlen(s);
for(int i = 0;i < lens;i++){
dp[i][i] = 1;dp[i][i-1] = 1;
}
cnt[1] += lens;
for(int len = 2;len <= lens;len++){
for(int i = 0;i < lens;i++){
int j = i + len - 1;
if(j >= lens) break;
if(s[i] != s[j] || dp[i + 1][j - 1] == 0){
dp[i][j] = 0;
else
dp[i][j] = dp[i][i + (len / 2) - 1] + 1;
cnt[dp[i][j]]++;
}
}
for(int i = lens;i >= 1;i--){
cnt[i] += cnt[i + 1];
}
for(int i = 1;i <= lens;i++){
printf("%d ",cnt[i]);
}
return 0;
}