2015 UESTC Training for Search Algorithm & String - M - Palindromic String【Manacher回文串】
O(n)的复杂度求回文串:Manacher算法
定义一个回文值,字符串S是K重回文串,当且仅当S是回文串,且其长度为⌊N/2⌋的前缀和长度为⌊N/2⌋的后缀是K−1重回文串
现在给一个2*10^6长度的字符串,求其每个前缀的最大回文值之和。
设dp[i]为长度为i的前缀的最大回文值。
当长度为i的前缀的字符串是回文串的时候,有:dp[i]=dp[i/2]+1
若不是回文串 dp[i]=0
接下来就是怎么样快速的判断回文串了,推荐算法Manacher算法。
Manacher算法先对字符串进行修改 如 aba -> $#a#b#a#
那么该怎么用DP求?
显然一下几点是满足的:
如果某个前缀是回文串,该前缀的末端一定是字符#,(因为第一个符号是#)
故对于不是字符#的位置,它的dp值一定为0
如果最大延伸数组p[i]=i,即向左正好延伸到最左边,那么1~p[i]+i-1一定是一个回文前缀
若第i位是#号 : dp[mx]=dp[i] 其中mx=p[i]+i-1
对于不是#的情况 : dp[mx]=dp[i-1] 其中mx=p[i]+i-1
#include<bits/stdc++.h> #define eps 1e-9 #define FOR(i,j,k) for(int i=j;i<=k;i++) #define MAXN 4000005 #define MAXM 40005 #define INF 0x3fffffff #define PB push_back #define MP make_pair #define X first #define Y second #define lc (k<<1) #define rc ((k<<1)1) using namespace std; typedef long long LL; int i,j,k,n,m,x,y,T,ans,big,cas,num,len; bool flag; int p[MAXN],dp[MAXN]; char str[MAXN],s[MAXN]; void kp() { int i; int mx = 0; int id; for(i=n; str[i]!=0; i++)//清除n后边多余的部分 str[i] = 0; //没有这一句有问题。。就过不了ural1297,比如数据:ababa aba for(i=1; i<n; i++) { if( mx > i ) p[i] = min( p[2*id-i], p[id]+id-i ); //因为是从左往右扫描的这里i>id, 2*id-i是i关于id的对称点,该对称点在id的左端 //p[id]+id是描述中的mx,即id向右延伸的端点位置 //显然向右延伸是可能超出mx的,所以要有下边的for循环 else p[i] = 1; for(; str[i+p[i]] == str[i-p[i]]; p[i]++); if( p[i] + i > mx )//更新mx与id,因为mx是向右延伸的最大长度,所以实时更新 { mx = p[i] + i; id = i; } } } void init()//处理字符串 { int i, j, k; str[0] = '$'; str[1] = '#'; for(i=0; i<n; i++) { str[i*2+2] = s[i]; str[i*2+3] = '#'; } n = n*2+2; s[n] = 0; } int main() { scanf("%s",s); n=strlen(s); init(); kp(); for (i=2;i<n;i++) { if (p[i]==i) { int mx=p[i]+i-1; if (str[i]!='#') { dp[mx]=max(dp[mx],dp[i-1]+1); }else dp[mx]=max(dp[mx],dp[i]+1); } } int sum=0; for (i=0;i<n;i++) sum+=dp[i]; printf("%d\n",sum); return 0; }