马拉车算法当然不是马拉着车的奇奇怪怪的东西,是Manacher’s Algorithm的音译。 Manacher’s Algorithm马拉车算法是一种可以在O(n) 线性时间内求最长回文子串的算法(Manacher是人名,发明者)。所谓的回文也就是正读反读都是一样的,比如noon 、level ,那在一个字符串中找最长的回文子串,一般使用中心扩展法,也就是枚举每一个字符为对称中心,然后向左右延伸并判断是否相等,但这种方法的时间复杂度是O(n^2)。
分析中心扩展法效率差的原因,是因为它做了大量重复计算,比如字符串aaaaaaaaa 、abababa ,也就是最坏的情况——各个回文子串相互重叠。而马拉车算法则通过一些空间记录和回文对称的特点来避免这种重复计算。
首先需要解决字符串长度奇偶时,对称中心不一致的问题。如串aba 和abba 都是回文串,但是它们的对称中心分别是不一致的,前者是b ,后者是位于b 中间。
我们在每个字符前后加上一个’#‘符,并且为了后续不越界和求原下标方便,我们让新字符串t 的下标从1开始,即下标0处也赋’#’。这样处理后字符串的长度就都是奇数了,对称中心自然也都是二分之一处的字符,不会再是位于两字符中间的情况。
现在我们引用一个数组p[]来表示中心扩展的最大长度,而它就是去掉’#'后原回文子串的长度,也就是我们求的答案。
以字符串ababaab 为例,通过P 的下标i 减去P[i] 再除以2 就能求出原回文子串,如下标8 处P[8]=3 ,(8-3)/2=2 ,即在原串S 中下标2 开始长度为3的子串是一个回文子串(aba )。
前面都是准备工作,下面才是马拉车的核心算法,它充分利用了回文的对称性,比如说对于回文串ababa ,不难发现它的p[] 数组也是关于中心对称的,如下图:
但这只限于回文串中可以这样直接镜像,还需要推导至一般情况,仍以串ababaab 为例。 设mid
为对称中心,r 表示回文串的右边半径(r=mid+p[i] ),下标i 即当前要求的p[i] ,下标j 表示i 关于中心mid 镜像对称的下标,分如下三种情况:
同时注意更新mid 和r ,也就是当更新p[i] 后,求出新的r(p[i]+i) ,若大于旧的r ,就更新mid 和r,即设置当前点i 为新的对称中心。
(
插播反爬信息)博主CSDN地址:https://wzlodq.blog.csdn.net/
#include <iostream>
#include <string>
using namespace std;
const int maxn = 1000006;
int p[maxn];
string s, t;
int main() {
cin >> s;
int n = s.size();
t = "#";
for (int i = 0; i < n; i++) {
t += '#';
t += s[i];
}
t += '#';
int m = t.size();
int mid = 0, r = 0;
int len = 0, st = 0;//回文串长度和起始位置
for (int i = 1; i < m - 1; i++) {
int j = mid * 2 - i;
if (i < r) //右边半径内
p[i] = min(r - i, p[j]);//防止超出r
else p[i] = 0; //>=r情况
while (t[i + 1 + p[i]] == t[i - 1 - p[i]])
p[i]++; //中心扩展
if (i + p[i] > r) { //更新r
mid = i;
r = i + p[i];
}
if (p[i] > len) { //顺便更新答案
len = p[i];
st = (i - p[i]) / 2;
}
}
cout << len << "\n" << s.substr(st, len);
return 0;
}
注意时间复杂度是O(n) ,当里面的while循环中心扩展后,下次循环是不会再访问到这些节点的,会直接用对称性。
题目描述 给出一个只由小写英文字符 \texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,…y,z 组成的字符串 SS ,求 SS 中最长回文串的长度 。 字符串长度为 nn。 输入格式 一行小写英文字符 \texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,…y,z 组成的字符串 SS。 输出格式 一个整数表示答案。 输入输出样例 输入 #1 aaa 输出 #1 3 说明/提示 1≤n≤1.1×107
#include <iostream>
#include <string>
using namespace std;
const int maxn=32000007;
int p[maxn]={0};
string s,t;
int main(){
cin>>s;
int n=s.size();
t="#";
for(int i=0;i<n;i++){
t+='#';
t+=s[i];
}
t+='#';
int m=t.size();
int mid=0,r=0,ans=0;
for(int i=1;i<m-1;i++){
int j=mid*2-i;
if(i<r)
p[i]=min(r-i,p[j]);
while(t[i+1+p[i]]==t[i-1-p[i]])
p[i]++;
if(i+p[i]>r){
mid=i;
r=i+p[i];
}
if(p[i]>ans)ans=p[i];
}
cout<<ans;
return 0;
}
题目描述 艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。 拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。 一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。 雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。 现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。 输入格式 输入为标准输入。 第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。 接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。 输出格式 输出为标准输出。 输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。 输入输出样例 输入 #1 5 3 ababa 输出 #1 45 说明/提示 样例说明 和谐小群体女生所拿牌子上写的字母从左到右按照女生个数降序排序后为ababa, aba, aba, bab, a, a, a, b, b,前三个长度的乘积为 。
跑一遍马拉车,并使用cnt[]数组记录回文串长度对应数量,过滤奇数的,从大往小循环即可,另外注意直接乘会超时,用快速幂。
#include <iostream>
#include <string>
using namespace std;
const int maxn = 2000006;
const int mod = 19930726;
typedef long long ll;
int p[maxn], cnt[maxn];
string s, t;
ll n, k;
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1)res = (res * x) % mod;
x = (x * x) % mod;
y >>= 1;
}
return res;
}
int main() {
cin >> n >> k >> s;
t = "#";
for (int i = 0; i < n; i++) {
t += '#';
t += s[i];
}
t += '#';
ll m = t.size();
int mid = 0, r = 0;
for (int i = 1; i < m - 1; i++) {
int j = mid * 2 - i;
if (i < r)
p[i] = min(r - i, p[j]);
else p[i] = 0;
while (t[i + 1 + p[i]] == t[i - 1 - p[i]])
p[i]++;
if (i + p[i] > r) {
mid = i;
r = i + p[i];
}
if (p[i] % 2)cnt[p[i]]++;
}
ll sum = 0, ans = 1;
for (int i = n; i >= 1; i--) {
if (i % 2 == 0)continue;
sum += cnt[i];
ll cur = min(sum, k);
k -= sum;
ans = (ans * qpow(i, cur)) % mod;
if (k < 0)break;
}
if (k > 0)ans = -1;
cout << ans;
return 0;
}