http://acm.hdu.edu.cn/showproblem.php?pid=4333
拓展kmp,打算在写几道再讲,现在只是理解了大概思想qwq。
给定一个字符串,可以把它后面的字母往前放,问你有多少大于原数,小于原数,等于原数的。qwq
暴力保存了一个,mle。毕竟1e5
后来看了正解,真的不错。原串乘2 是这种情况
模式串(len) 和 目标串的子串(长度也为len)比较,如图 上面。
打个比方 123 和 124 前面相等,只比较后面的。相当于先求他和目标串的前缀长度x,然后一个一个是 x+1,另一个是x+i+1.(在母串中)。
还有一个 代码里 查找 模式串循环节那个,在图中下面那个,大概就是 模式串长度能整除 黑色卡着的部分。那么就能算出来qwq。。好神奇
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
/* 拓展kmp是对kmp的一种拓展,
用来求 目标串 和模式串的 后缀 的最长公共前缀。
我们发现,当最长公共前缀为目标串的长度时,其实就是判断
模式串是否在模式串中出现的次数。
最长公共前缀标定 extends[]数组,
如果采用暴力的计算方法,是不可取的。
这里面 利用了 前缀之间的性质。
本题还有一个厉害的地方,那就是计算 一个字符串的循环节。
*/
const int maxn=2e5+100;
int nex[maxn];
int extand[maxn];
int getnext(int len,char s[]){
nex[0]=-1;
int i=0;int k=-1;
while(i<len){
if(k==-1||s[i]==s[k]){
nex[++i]=++k;
}
else
k=nex[k];
}
// for(int i=0;i<=len;i++)
//cout<<nex[i]<<endl;
}
void getnext(char T[]){
int len=strlen(T),a=0;
extand[0]=len;
while(a<len-1&&T[a]==T[a+1]) a++;
extand[1]=a;
a=1;
for(int k=2;k<len;k++){
int p=a+extand[a]-1,L=extand[k-a];
if((k-1)+L>=p){
int j=(p-k+1)>0? (p-k+1):0;
while(k+j<len&&T[k+j]==T[j]) j++;
extand[k]=j;
a=k;
}
else extand[k]=L;
}
}
int main(){
ios::sync_with_stdio(false);
char s[maxn];
int t;
cin>>t;
for(int tt=1;tt<=t;tt++){
cin>>s;
int len=strlen(s);
getnext(len,s);
for(int i=0;i<len;i++){
s[i+len]=s[i];
} s[len*2]='\0';
//cout<<s<<endl;
int siz=len-nex[len];
int k;
//cout<<nex[len]<<endl;
if(!(len%siz)){
k=len/siz;
}
else
k=1;
getnext(s);
int l=0,r=0,mid=0;
for(int i=0;i<len;i++){
if(extand[i]>=len) mid++;
else if(s[extand[i]]>s[extand[i]+i]) l++;
else if(s[extand[i]]<s[extand[i]+i]) r++;
}
cout<<"Case "<<tt<<": "<<l/k<<" "<<mid/k<<" "<<r/k<<endl;
}
return 0;
}