Day6 字符串(二)
简单字符串(二)
.. -....- .-.. .. -.- . -....- -..- .. . .--- .. .- -.. --- -. --.
最小表示法、Manacher、扩展KMP、AC自动机
KMP
void getnxt2(){
int k=-1, j=0;
while(j<m){
if(k==-1||s[k]==s[j]) nxt[++j]=++k;
else k=nxt[k];
}
}
void getnxt(){
int k=0;
for(int i=2;i<=lenB;++i){
while(k&&s[i]!=s[k+1]) k=nxt[k];
if(s[i]==s[k+1]) k++;
nxt[i]=k;
}
}
void KMP(){
int k=0;
for(int i=1;i<=lenA;++i){
while(k&&s2[i]!=s[k+1]) k=nxt[k];
}
}
HDU3336 Count the string
给出一串字符串s。
求出所有s的前缀在s中出现次数之和。
字符串长度 <= 200000。
对于一个前缀,他本身是s的子串,所以每一个前缀都至少出现了1次。考虑nxt[i]
,对于s的前缀s[1...i]
,所有nxt
值等于i
的地方一定是这个前缀出现的结尾。然后考虑nxt
的叠加。
考虑:
-----**-----
nxt[14]=7
nct[7]=1
考虑这个:
-*------*
nxt[13]=4
nxt[4]=1
=>'*'出现于s[10]和s[13]
ZOJ1905 Power String 字符串的最短循环串
求字符串的最短的循环节。
即S 的最短循环节是 A 当且仅当 S=AAA..AAA。
首先,A的长度一定是S的长度的因子。枚举因子,因子个数的一定小于\(2*sqrt{n}\)。然后hash。
事实上,一个字符串的最短循环节的长度一定是\(n-nxt[n]\)。加强问题变为求s所有前缀的最短循环串长度只和,可用此结论。例串:
--*--*--*--*
设最短循环串长度为x,那么s[1]=s[n-x+1],s[2]=s[n-x+2]??
ZOJ1905 Power Strings 改编
求字符串的最短的循环节。
即S 的最短循环节是 A 当且仅当 S 是 AAA..AAA 的 substring。
??
POI2006 OKR-PERIODS OF WORDS
对于一个仅含小写字母的字符串 a,p 为 a 的前缀且 \(p\ne a\),那么我们称 p 为 a的 proper 前缀。
规定字符串 Q(可以是空串)表示 a 的周期,当且仅当 Q 是 a 的 proper 前缀且 a 是 Q+Q 的前缀。
例如 ab 是 abab 的一个周期,因为 ab 是 abab 的 proper 前缀,且 abab 是 ab+ab 的前缀。
求给定字符串所有前缀的最大周期长度之和。
s: a b a b c a b a b a b a b c a b a b
nxt:0 0 1 2 0 1 2 3 4 3 4 3 4 5 6 7 8 9
T: 0 0 2 2 0 5 5 7 7 9 9 11 11 9 14 14 16 16
可以发现对于前缀ababcabab,他的周期为ababcab,这样:
ababcabab
ababcabababcab
也就是说ababcab的后缀ab和周期ababcab的前缀匹配了。我们只需求出这个匹配的最小值,就能得到周期的最大值。假设前缀为s[1...i],可以发现这个匹配就是递归nxt[i]得到的非零的最小值。
POJ2752 Seek the Name, Seek the Fame
给一个字符串,问你前缀和后缀相同的位置有哪些。
字符串长度<= 400000。
真就递归nxt。
a b a b c a b a b a b a b c a b a b
0 0 1 2 0 1 2 3 4 3 4 3 4 5 6 7 8 9
x
x
x
x
代码:
const int maxn = 400110;
int n, nxt[maxn];
char s[maxn];
void init(){
memset(nxt, 0, sizeof(nxt));
}
void get_nxt(){
memset(nxt, 0, sizeof(nxt));
int k=0;
for(int i=2;i<=n;++i){
while(k&&s[k+1]!=s[i]) k=nxt[k];
if(s[i]==s[k+1]) k++;
nxt[i]=k;
}
}
void doit(int pos){
if(!pos) return;
doit(nxt[pos]);
printf("%d ", pos);
}
int main(){
while(~scanf("%s", s+1)){
n=strlen(s+1);
get_nxt();
doit(n);
printf("\n");
}
return 0;
}
拓展KMP
给定两个字符串S和T,分别长度为n和m,下标从1开始。求对于S的每一个后缀S[i...n],他与T的最长公共前缀的长度。
设extand[i]表示S的后缀S[i...n]与T的最长公共前缀的长度。
(逐渐看不见屏幕
如果S不和T匹配而是和自身匹配的话,nxt也就是extand有另一个名字叫Z函数,算法代码如下:
vector<int> Get_Z(string s){
int n=(int)s.length();
vector<int> z(n);
for(int i=1,l=0,r=0;i<n;++i){
if(i<=r&&z[i-l]<r-i+1){
z[i]=z[i-l];
}
else{
z[i]=max(0,r-i+1);
while(i+z[i]<n&&s[z[i]]==s[i+z[i]])z[i]++;
}
if(i+z[i]-l>r) l=i,r=i+z[i]-1;
}
return z;
}
void GetExtand(){
}
HDU2594 Simpsons’ Hidden Talents
模板。(逐渐看不见屏幕
2019 Multi-University HDU Day 5 D
(看不见屏幕
Manacher
int n, d[maxn];
char s[maxn];
string in;
int main(){
cin >> in;
for(int i=0;i<in.size();++i){
s[++n]=in[i];
s[++n]='#';
}
n--;
int l=1, r=0;
for(int i=1;i<=n;++i){
if(i>r){
int j=1;
while(i-j>=1&&i+j<=n&&s[i-j]==s[i+j]) j++;
d[i]=j-1;
}
else{
int j=l+r-i;
j=min(d[j], j-l)+1;
while(i-j>=1&&i+j<=n&&s[i-j]==s[i+j]) j++;
d[i]=j-1;
}
if(d[i]+i>=r) r=i+d[i], l=i-d[i];
if(i+d[i]>=1&&i+d[i]<=n&&s[i+d[i]]=='#') d[i--];
ans=max(ans, d[i]);
}
printf("%d\n", ans+1);
}
2018 ICPC Nanjing Regional Contest M
给出两个字符串 s ,t ,求有多少的数对 (i,j,k) 满足 si,… ,s_j,t_1,… ,t_k 是回文串,且 j-i+1>k。
字符串长度均为 10^5。
链接回文串。
先给s跑一遍马拉车,再用t的前缀和倒序的s跑KMP。
NOWCODER14894 最长回文
从字符串𝐴中取出可空子串[𝑙1,𝑟1],从字符串𝐵中取出可空子串[𝑙2,𝑟2],要求𝑟1=𝑙2且是回文串,长度最长能是多少。
字符串长度 <=10^5
和上一题差不多,构成的回文串也是由中间的短回文串和A和B匹配的一段构成。 先Manacher,再exKMP。
POI2007 Axes of Symmetry
求多边形的对称轴条数。顶点<=1e5。|坐标|<=1e9。
破多边形的边长和角度为序列,然后跑Manacher。即序列中的每一个元素是多边形的一条边长或是两边之间的夹角的大小。对于一个夹角,如果多边形上这个角的角平分线所在的一条直线是一条对称轴,与其相邻的两条边一定长度相等,再往外一层相邻的两个夹角一定大小相同...对应在序列上即这个夹角是长度为\(2*n\)的回文串的中心。对于边同理。我们要判断所有的边和角,于是我们将序列复制一份接到后面,长度变为\(4*n\)然后从长度为n处开始判断,只要以当前元素为中心存在长度为\(2*n\)的回文串,则多边形关于当前元素对称(角分线/中垂线)
阿狸的打字机
对所有的打印字符串建一个AC自动机。
如果顺着节点R的fail能走到L,那么L串一定在R串中出现过。
由于AC自动机是静态的,用线段树维护就没有什么必要性了,毕竟多一个log(多年前的老爷机不一定跑得了)。(??
无序运动
(彻底掉线
本文来自博客园,作者:咕咕坤,转载请注明原文链接:https://www.cnblogs.com/GuguKun/p/15046978.html