问题
现在针对一个字符串 s1 我们要找到他的最长回文子串,该如何去设计这个算法。
回文串:
就是一个字符串颠倒后与原来的串相同,比如 aabbcbbaa,这是个回文串,aabbaa这也是一个串
现在我们以“aabbcbbaa"为例子,要说这个串是回文串的一种判断方法是 以c为中心,向两边扩展,左右相等,判断是不是。
那么针对以上问题,我们可以枚举每个位置,以这个位置为中心,向外扩展,扩展到最大,维护一个最长的回文串,但是如果是偶数串的话,那么在没有中心我们要怎么办,方法是在每两个字符之间插入一个与问题毫无相关的字符
比如 串"aabbaa",在插入 ’&‘ 后变成 &a&a&b&b&a&a& ,这样串就变成了一个奇数串,这个新回文串长度为 13 原串长度为 6
那么串"aabaa"同上述操作后变成”&a&a&b&a&a&"也是一个奇数串,新回文串长度为11,原串长度为5,
即原串的长度l1与新串长度l2的关系是 l1=l2/2;
对串进行上述变换的代码是
#include<bits/stdc++.h>
using namespace std;
signed main()
{
string s; cin >> s;
int n = 0;
string a;
a+= '^', a+= '&';
for (int i = 0; i < s.size(); i++)
{
++n;
a += s[i];
++n;
a += '&';
}
cout << a << endl;
return 0;
}
上面得到的新串有效位置下标从1开始(小心点)
原串是s,新串是a(本文一下都是这样,提示一下,下文不做解释)
对于每个位置是O(n) 里面每次维护也是O(n),达到一个O(n*n)的复杂度
我们既然说了以每个位置向两端扩展到底怎么扩展呢,
比如 aabbaa-> $a$a$b$b$a$a$
我们以箭头哪里开始 ,此位置的坐标是7 ,判断 a[7-1]==a[7+1]? 成立我们用一个 数 r(初始时是0)使 r++; 一直循环 到不成立 ,转换成代码使
int maxa = -0x3f3f3f3f;
for (int i = 1; i <= n; i++)
{
int j = 1,r=0;
while (i - j >= 1 && i + j <= n && a[i - j] == a[i + j])
{
++r;
++j;
}
maxa = max(maxa, r);//维护一个最大回文串
}
但是这个r最后肯定不是原串的最大 ,我们r/2,就是原串啦!!!你是不是这样想的,这个r的意义是回文串长度的一半,r*2得到的是新串中某个回文串的长度,在r*2/2的到的是原串的长度,即
新串中r即使原串的回文串长度
那整体的代码就是
#include<bits/stdc++.h>
using namespace std;
signed main()
{
string s; cin >> s;
int n = 1;
string a;
a+= '^', a+= '&';
for (int i = 0; i < s.size(); i++)
{
++n;
a += s[i];
++n;
a += '&';
}
int maxa = -0x3f3f3f3f;
for (int i = 1; i <= n; i++)
{
int j = 1,r=0;
while (i - j >= 1 && i + j <= n && a[i - j] == a[i + j])
{
++r;
++j;
}
maxa = max(maxa, r);//维护一个最大回文串
}
cout <<"输入的字符串的最大回文字串长度为 " << maxa << endl;
return 0;
}
这个算法枚举每个中心,里层在扩展两边,复杂度肯定比o(n)大,现在如果说我们把上面枚举的每个中心都放到一个r数组里,记录下来
那么 aabaa ->$a$a$b$a$a$->r数组:0 1 2 1 0 5 0 1 2 1 0
下面给出一个图解
在对以b为中心枚举回文串后,如果在往右边枚举下一个中心,在以b为中心向外扩展的里面 那么我们在以这个为中心扩展时,在b的左边是否以经进行了相同的操作
//马拉车模板
#include<bits/stdc++.h>
using namespace std;
int r[1000100];
signed main()
{
string s; cin >> s;
int n = 1;
string a;
a+= '^', a+= '&';
for (int i = 0; i < s.size(); i++)
{
++n;
a += s[i];
++n;
a += '&';
}
int maxa = -0x3f3f3f3f;
int mid = 0, R = 0;
for (int i = 1; i <= n; i++)
{
r[i] = (i <= R) ? min(r[2*mid - i], R - i + 1) : 1;
while (a[i-r[i]] == a[i+r[i]])
{
++ r[i];
}
if (i + r[i] - 1 >= mid)
{
R = i + r[mid = i] - 1;
}
maxa = max(maxa, r[i]-1);
}
cout <<"输入的字符串的最大回文字串长度为 " << maxa << endl;
return 0;
}
这个算法实际的复杂度是O(n)