题目大意:给出一个长为n的仅由小写英文字母组成的字符串,求它的回文串划分的元素的最小个数,并按顺序输出此划分。所谓回文划分就是将原字符串拆分成若干个回文子字符串。
Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
数据规模:n<=4000。
理论基础:回文串定义:“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
题目分析:用dp[i]表示前i个字符的回文串划分的元素的最小值。pos[i]表示取得最小值时最后一个回文串所在位置的前一个位置。odd[i]表示以s[i]为中心的最长奇数回文串的偏移量,even[i]表示以s[i]==s[i+1]为中心的最长偶数回文串的偏移量。
那么,dp[i]=min(dp[i-k]+1,k为所有的以i为结束点的回文串的长度)。
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
#define N 4000
int n,tot,odd[N+1],even[N+1],pos[N+1],d[N+1],dp[N+1],print[N+1];
char s[N+5];
int main()
{
cin >> &s[1];
n=strlen(&s[1]);
for (int i=1;i<=n;++i)
{
int l=i,r=i;
while ((s[l] == s[r]) && l && (r <= n))
{
odd[i]++;
--l,++r;
}
l=i,r=i+1;
while (l && (r <= n) && (s[l] == s[r]))
{
even[i]++;
--l,++r;
}
}
for (int i=1;i<=n;++i)
{
dp[i]=dp[i-1]+1;
pos[i]=i-1;
for (int j=(i+1)>>1;j;--j)
{
int p=i-(j<<1)+1,q=i-(((j<<1)-1)>>1);
if (odd[q] >= j)
{
if (dp[p]+1 < dp[i])
{
dp[i]=dp[p]+1;
pos[i]=p;
}
}
p=i-(j<<1),q=i-j;
if (p < 0) continue;
if (even[q] >= j)
{
if (dp[p]+1 < dp[i])
{
dp[i]=dp[p]+1;
pos[i]=p;
}
}
}
}
cout << dp[n] << endl;
while (n)
{
print[++tot]=n;
n=pos[n];
}
while (tot)
{
for (int i=print[tot+1]+1;i<=print[tot];++i)cout<<s[i];
--tot;
tot?cout << ' ':cout<<'\n';
}
return 0;
}
其中dp方法有些变化,不过大体算法不变,参照题目分析与代码即可理解。
by:Jsun_moon http://blog.youkuaiyun.com/Jsun_moon