有一个字符串S,求S最少可以被划分为多少个回文串。
例如:abbaabaa,有多种划分方式。
a|bb|aabaa - 3 个回文串
a|bb|a|aba|a - 5 个回文串
a|b|b|a|a|b|a|a - 8 个回文串
其中第1种划分方式的划分数量最少。
Input
输入字符串S(S的长度<= 5000)。
Output
输出最少的划分数量。
Input示例
abbaabaa
Output示例
3
状态转移:dp[i+j] = min(dp[i+j],dp[i-j-1]+1);(s[i+j]==s[i-j])
dp[i+j+1] = min(dp[i+j+1],dp[i-j-1]+1);(s[i+j+1]==s[i-j])
代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<ll, int> P;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-10;
const int maxn = 5e3+7;
const int mod = 1e9+7;
char s[maxn];
int dp[maxn];
int main(){
scanf("%s",s+1);
int n = strlen(s+1);
for(int i = 1;i <= n;i++){
dp[i] = INF;
}
dp[0] = 0;
for(int i = 1;i <= n;i++){
int j = 0;
while(i-j>=1&&i+j<=n&&s[i-j]==s[i+j]){
dp[i+j] = min(dp[i+j],dp[i-j-1]+1);
j++;
}
j = 0;
while(i-j>=1&&i+j+1<=n&&s[i-j]==s[i+j+1]){
dp[i+j+1] = min(dp[i+j+1],dp[i-j-1]+1);
j++;
}
}
cout<<dp[n]<<endl;
return 0;
}
经验:看题的时候想到了马拉车,然后想到拓展kmp,然后是后缀数组,当然都只是略过,没深入想,看到n = 5000的时候就有暴力的想法,然后想到最小,然后然后就有了dp的想法。所以说,数据范围还是很重要的。
回想起的知识点:
字符串的处理算法:
1、kmp
2、哈希
3、拓展kmp
4、马拉车(和3的算法思路很像,和3一样都是很强的算法,都是O(n)理论最优算法)
5、后缀数组(用处很大,但是用到的比较少)(还有一个是哈希后缀数组。。)