题目地址: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;
}
思路比较简单,而且效率很好。空间复杂度为O(1),时间复杂度为O(N^2)
传说中的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算法,人家讲的也很清晰。
还是需要多多练习啊。