题目地址:http://ac.jobdu.com/problem.php?pid=1528
回文大家应该都知道了,正反看都一样,题目意思就是求一个字符串的子串,要求这个子串是回文。
最简单的办法:暴力法:(果断的TLE了)
判断s[i,j]是不是回文
#include<iostream> #include<string> #include<string.h> #include<cstdio> #include<ctype.h> #include<algorithm> using namespace std; char buf[200001], res[5005]; int main() { while(~scanf("%s", &buf)) { int m = 0, len, max = 0; int i, j, k; len = strlen(buf); for(i = 0; i < len; ++i) { for(j = i; j < len; ++j) { int ok = 1; for(k = i; k <= j; ++k) { if(buf[k] != buf[i + j - k]) { ok = 0; break; } } if(ok && j - i + 1 > max) max = j - i + 1; } } printf("%d\n",max); } return 0; }
LP[i][j]=true 表示buf[i...j]是回文
状态转移方程:LP[i][j]=p[i+1][j-1] if(buf[i]==buf[j]
如果buf[i+1...j-1]是回文,那么如果buf[i]==buf[j],LP(i,j)=LP(i+1,j-1)
#include<iostream> #include<string> #include<string.h> #include<cstdio> #include<ctype.h> #include<algorithm> using namespace std; char buf[200001], res[5005]; bool LP[20001][20001]; int main() { int longestBegin=0,maxLen=1; while(~scanf("%s", &buf)) { int length = strlen(buf); memset(LP,0,sizeof(LP)); for(int i=0;i<length;i++) LP[i][i]=1; for(int i = 0; i < length-1; ++i) { if(buf[i]==buf[i+1]) { LP[i][i+1]=1; longestBegin=i; maxLen=2; } } /*依次求LP[i][i+2]...LP[i][i+n-1]等*/ for(int len=3;len<=length;len++)//LP[i...i+len-1]的状态 for(int i=0;i<length-len+1;i++) { int j=i+len-1; if(buf[i]==buf[j] && LP[i+1][j-1]) { LP[i][j]=1; longestBegin=i; maxLen=len; } } printf("%d\n",maxLen); } return 0; }
下面这段文字摘自ssjhust123的博客:(顺便学习了)
回文字符串是以字符串中心对称的,如abba以及aba等。一个更好的办法是从中间开始判断。
一个长度为N的字符串可能的对称中心有2N-1个,至于这里为什么是2N-1而不是N个,是因为可能对称的点可能是两个字符之间,比如abba的对称点就是第一个字母b和第二个字母b的中间。因此可以依次对2N-1个中心点进行判断,求出最长的回文字符串即可。根据该思路可以写出下面的代码。
代码如下:
#include<iostream> #include<string> #include<string.h> #include<cstdio> #include<ctype.h> #include<algorithm> using namespace std; char buf[200001], res[5005]; //bool LP[20001][20001]; int expandAroundCenter(int l, int r,int n) { // int n = s.length(); while (l>=0 && r<=n-1 && buf[l]==buf[r]) { l--, r++; } return r-l-1; } int longestPalindrome3() { string s(buf); int n = strlen(buf); if (n == 0) return 0; int longest=1; //string longest = s.substr(0, 1); for (int i=0; i<n; i++) { int p1 = expandAroundCenter( i, i,n); //以位置i为中心的最长回文字符串 if (p1 > longest) longest = p1; int p2 = expandAroundCenter( i, i+1,n); //以i和i+1之间的位置为中心的最长回文字符串 if (p2 > longest) longest = p2; } return longest; } int main() { int longestBegin=0,maxLen=1; while(~scanf("%s", &buf)) { printf("%d\n",longestPalindrome3()); } return 0; }
传说中的Manacher算法,时间复杂度可以降到O(N)【不过实际测试的时候发现M法耗时80ms,而中心法耗时仅为60ms,可能是我写的不太好吧】
网上有很多解释,不过都不怎么清楚,
强烈建议大家去这个看看,绝对清晰明了,一下就会了:http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
代码如下:
#include<vector> #include<iostream> #include<string> using namespace std; string preProcess(string s) { int n = s.length(); if (n == 0) return "^$"; string ret = "^"; for (int i = 0; i < n; i++) ret += "#" + s.substr(i, 1); ret += "#$"; return ret; } int P[400005]; char str[400005]; char s[200001]; void preProcess() { int n = strlen(s); if (n == 0) return; int k=0; str[k++]='^'; for (int i = 0; i < n; i++) str[k++]='#',str[k++]=s[i]; str[k++]='#',str[k++]='$'; str[k]='\0';//这个需要加,这样就避免了每次调用memset清空了。 } int longestPalindrome() { preProcess(); int n = strlen(str); memset(P,0,sizeof(P)); int C = 0, R = 0; for (int i = 1; i < n-1; i++) { int i_mirror = 2*C-i; // equals to i' = C - (i-C) P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0; // Attempt to expand palindrome centered at i while (str[i + 1 + P[i]] == str[i - 1 - P[i]]) P[i]++; // If palindrome centered at i expand past R, // adjust center based on expanded palindrome. if (i + P[i] > R) { C = i; R = i + P[i]; } } // Find the maximum element in P. int maxLen = 0; for (int i = 1; i < n-1; i++) { if (P[i] > maxLen) { maxLen = P[i]; } } //delete[] P; return maxLen; } int main() { while(scanf("%s", s)!=EOF) { printf("%d\n", longestPalindrome()); } return 0; }
好了,倒腾了一天,总算是搞完了这个东西了,不过话说昨晚九度的比赛,第一题真是坑爹,到现在各种PE了N次,还是不过,哎,算了算了,不去纠结这些无聊的东西了,还是好好看看有实际意义的东西吧。
最后的这道题还是挺有意思的,特别是学会了Manacher算法,人家讲的也很清晰。
还是需要多多练习啊。