题目描述
如果一个字符串正着读和倒着读是一样的,则称它是回文的。
给定一个长度为N的字符串S,求他的最长回文子串的长度是多少。
输入格式
输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个小写字符的形式给出。
输入以一个以字符串“END”(不包括引号)开头的行表示输入终止。
输出格式
对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。
每个输出占一行。
样例
输入样例:
abcbabcbabcba
abacacbaaaab
END
输出样例:
Case 1: 13
Case 2: 6
算法
用哈希的 $O(n)$ 算法
既然用了Hash,为什么还要用二分?
直接带入上一个位置的回文串长度,用 Hash 检验是否能达到这个长度,如果可以,再进行延长。
C++ 代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define boost ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define fo(v,a,b) for(int v=(a); v<=(b); v++)
#define fr(v,a,b) for(int v=(a); v>=(b); v--)
#define rng(v,a,b) for(int v=(a); v<(b); v++)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template<typename T> T& chmax(T& a, T b) { a = a > b ? a : b; return a;}
template<typename T> T& chmin(T& a, T b) { a = a < b ? a : b; return a;}
const int maxn=1e6+6,P=131;
char s[maxn],a[maxn*2];
ull H1[maxn*2],H2[maxn*2],g[maxn*2];
int main()
{
int T=0;
while(cin >> (s+1) && strcmp(s+1, "END")) {
cout << "Case " << ++T << ": ";
int len = strlen(s+1), tl=0;
a[++tl] = '#';
fo(i,1,len) {
a[++tl] = s[i]; a[++tl] = '#';
}
a[tl+1] = '\0'; len=tl;
g[0] = 1;
fo(i,1,len) {
H1[i] = H1[i-1]*P+a[i]; g[i] = g[i-1]*P;
}
fr(i,len,1) H2[i] = H2[i+1]*P+a[i];
int ans=0,l;
fo(i,1,len) {
l=ans;
if(i+l>=len || i-l<1) break;
if(H1[i+l]-H1[i-1]*g[l+1] != H2[i-l]-H2[i+1]*g[l+1]) continue;
while(a[i+l+1] == a[i-l-1] && i+l+1<=len && i-l-1>0) l++;
chmax(ans,l);
}
cout << ans << '\n';
}
return 0;
}
hh 这都能过。。。你for循环里面还套了一个while,最会情况下时间复杂度应该是是 O(n * n)呀!因为里面的while最坏可能跑 n / 2 次,这题可能数据很水这代码才能过吧!按理说应该会超时。
不对的吧,因为l是顺承上一次的长度,并不是每一个i都能跑满,一共n次延长均摊开来就是O(n)
l是单调递增的,所以循环内的l++一共只会执行n次
明白了,谢谢大佬
%%%%
好有道理,666
# %
我想问一下,为啥这道题不可以用尺取的方法
666
stO Otz
可以在需要更新的时候二分。二分查找应该是比直接延长快的。
需要更新时也不应该二分。要优化的话应该倍增优化,不然用二分优化可能退化到O(nlogn)
太强了
这里l记录的就是前缀最大值,应该不用ans了%%%要预处理hash,理论上效率没有Manacher高(有没有dalao测一下),不过比较好写%%%%%
本人打了一个Manacher(代码1,约800B)和本题解标程(代码2,约1.6KB,删去一些无关的东西后约1.2KB)的比对了一下,然后跑了20组随机数据(每组都是30个字符串,每个字符串长度为100w)。同时本人用自己的码风打了一遍题解的思路(代码3,约900B)。结果如下:
代码1总用时49635ms,代码2总用时84274ms,代码3总用时48117ms。
说明这个思路确实没有问题,速度和Manacher在随机数据下差不多(以本人码风),没有测过非随机的数据(毕竟随机数据中一般回文串都比较短,不能反映回文串长时的效率)。
比较好写的话,这个不好说。题解思路的代码长度要长一些,不过确实思路简单。.
太强了
呃。。。if(i+l>=len || i-l<1) break;
这里不可能i-l<1吧
%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%
%%%%%%%%%
%%%%%%%%%%%%%%%%%
似乎是尺取的思想,%%%
%%%
666啊,大佬
似乎是Manacher算法 https://blog.csdn.net/dyx404514/article/details/42061017
与Manacher算法不同的