问题描述
回文串是指aba、abba、cccbccc、aaaa这种左右对称的字符串。
输入一个字符串Str,输出Str里最长回文子串的长度。方法一:暴力求解
遍历每一个子串,再判断这个子串是不是回文串,最后判断这个串是不是最长的回文子串。
遍历子串的复杂度是O(n^2),判断是不是回文串的复杂度是O(n),所以这个算法的复杂度是O(n^3)。方法二:动态规划法
用一个二维的数组ai来表示从第i位到第j位的子串是不是回文串,在判断从i到j的子串是不是回文串时,可以先看i+1到j-1是不是回文串,再判断i位和j位是不是相同。这个算法中,遍历子串的复杂度仍然是O(n^2),但是判断是不是回文串的复杂度降到了O(1),所以这个算法的复杂度是O(n^2)。但是这个算法占据了O(n^2)的空间。
方法三:中心扩展法
顾名思义,任何一个回文串都有一个对称轴,从这个中心的位置开始,向两边扩展,可以得到以此为中心的最长回文串。但是要注意,这个对称轴的位置,可能是一个字符,也可能是两个字符中间。遍历对称轴的位置,复杂度是O(n),找到以此对称轴为中心的最长回文串,其复杂度是O(n),所以此算法的复杂度是O(n^2)。这个算法比动态规划好的地方是其空间复杂度只有O(1)。
#include#include using namespace std;#define LEN 1000int main(){ char str[LEN]; cin>>str; int len=strlen(str); int maxlen=0,mx; for(int i=0;i =0)&&(i+j mx?maxlen:mx; } for(int i=0;i =0)&&(i+j+1 mx?maxlen:mx; } cout< <
方法四:manacher算法
预处理
在字符串的开始加上一个'$'符,然后在每个字符中间插上一个'#'。比如,字符串ss='abac',处理之后是str='$#a#b#a#c#'。接下来的计算针对处理后的字符串。
len数组
然后定义一个len数组,len[i]表示的是以str[i]为中心的最长回文串的半径。
仍以上面的字符为例。str='$#a#b#a#c#',以str[0]为中心的最长回文串是'$',其半径是1;以str[4]为中心的最长回文串是'#a#b#a#',其半径是4;len数组为{1,1,2,1,4,1,2,1,2,1}。可以发现,len[i]-1的值,就是原字符串ss中对应的回文串的长度(以#为中心的是偶长度的回文串,以字符为中心的是奇长度的回文串)。计算len数组
算法的关键在于在计算len数组时,可以利用前面的结果进行优化。
引入变量maxright表示当前访问到的所有回文子串,所能触及的最右一个字符的位置;同时记录maxright所对应的回文串的对称轴的位置,记为pos。复杂度分析
考虑p的值的变化,在计算的过程中,p只会增加不会减少,当p增加到strlen(str)时,每个位置的len数组的值都可以立即计算得出。所以算法的复杂度是O(n)。
#include#include #include using namespace std;#define N 100004string str,ss;int len[2*N+1];int main(){ cin>>ss; str="$#"; for(unsigned int i=0;i i) len[i]=(len[j]>p-i)?(p-i):(len[j]); else len[i]=1; while(i+len[i] =0&&str[i+len[i]]==str[i-len[i]]) len[i]++; if(i+len[i]>=p) { pos=i; p=i+len[i]; } } int ans=0; for(int i=0;i =ans)?len[i]-1:ans; }// cout<