KMP简单应用
KMP简单应用
算法原理就不讲了,抄书没有意思请原谅我讲不清楚
下面给出几个
-
为最小循环元长度(不论是否能够完整循环)、 -
再套若干层 仍然满足
证明这些在下面的题里面
Power Strings
所求的循环元长度为
证明:
根据循环元的性质:
if(m%(m-nxt[m]))cout<<"1\n";
else cout<<m/(m-nxt[m])<<"\n";
练习题:Period
最长前缀
考虑转化问题。
首先对集合中的每一个串对源字符串进行匹配,就能得到所有的匹配成功区间
然后问题就化为:给定若干区间,求其最长能够覆盖区间的
设
void init(int id){
int m=strlen(a[id]+1);
for(int i=2,j=0;i<=m;i++){
while(j&&a[id][j+1]!=a[id][i])j=nxt[id][j];
if(a[id][j+1]==a[id][i])++j;
nxt[id][i]=j;
}
for(int i=1,j=0;i<=n;i++){
while(j&&b[i]!=a[id][j+1])j=nxt[id][j];
if(b[i]==a[id][j+1])++j;
if(j==m){
g[++num]={i-m+1,i};j=nxt[id][j];
}
}
}
void get(){
while(cin>>a[++cnt]+1){
if(a[cnt][1]=='.'){
--cnt;break;
}
}
while(cin>>b[++n]);
}
get();
for(int i=1;i<=cnt;i++)init(i);
int l=1;
f[0]=1;int ans=0;
sort(g+1,g+num+1);
for(int r=1;r<=n;r++){
while(l<=num&&g[l].r<=r){
f[r]|=f[g[l].l-1];++l;
}
if(f[r])ans=r;
}
Seek the Name, Seek the Fame
容易发现,对于
则
int k=m,cnt=0;
while(k){
num[++cnt]=k;
k=nxt[k];
}
while(cnt){
cout<<num[cnt--]<<" ";
}
字符串大师
根据Power Strings,容易知道
因为字典序最小,第一个字符肯定是a
然后考虑进行递推,设已经求出了
那么若
否则的话,根据
而且类比上一题,也是进行嵌套的都不行,也即:
int num=nxt[i-1];
while(num){
usd[a[num+1]-'a']=1;num=nxt[num];//这个位置不等
}
然后一个需要注意的点是,这个地方不能重复取开头的字符a,原因是
所以再枚举字符取最小就行
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int x;cin>>x;
nxt[i]=i-x;
}
a[1]='a';cout<<'a';
for(int i=2;i<=n;i++){
memset(usd,0,sizeof usd);
if(nxt[i]!=0){a[i]=a[nxt[i]];cout<<a[i];continue;}
int num=nxt[i-1];
while(num){
usd[a[num+1]-'a']=1;num=nxt[num];
}
for(int j=1;j<=25;j++){
if(!usd[j]){
a[i]=j+'a';
cout<<a[i];break;
}
}
}
}
基因改造
人类智慧 题
容易发现,本质上在这里,C是啥用没有的。。。
然后在这个定义的匹配规则中,等价于要字符串的结构一样才行。
也即给字符建立映射用字符集重组只会一样。
举个例子:1113221与4447664是本质相同的,重组之后都可以得到1112331
那么对于结构相同的字符,字符相对关系不变。也即任意一类字符都只会整体改变,则其距离始终不变
同理,显然知道了每一类字符的距离也就可以求出一个本质相同的字符串
那么就很明晰了,定义其权值为
则两个位置
使用KMP匹配即可。
cin>>t>>c;
while(t--){
num=0;
cin>>n>>m;
memset(pos,0,sizeof pos);
for(int i=1;i<=n;i++){
int x;cin>>x;
va[i]=(pos[x]?i-pos[x]:0);pos[x]=i;
}
memset(pos,0,sizeof pos);
for(int i=1;i<=m;i++){
int x;cin>>x;
vb[i]=(pos[x]?i-pos[x]:0);pos[x]=i;
}
for(int i=2,j=0;i<=m;i++){
while(j&&vb[i]!=vb[j+1]&& !(vb[j+1]==0&&vb[i]>j))j=nxt[j];
if(vb[i]==vb[j+1]||(vb[j+1]==0&&vb[i]>j))++j;
nxt[i]=j;
}
for(int i=1,j=0;i<=n;i++){
while(j&&(va[i]!=vb[j+1]&& !(vb[j+1]==0&&va[i]>j)))j=nxt[j];
if(va[i]==vb[j+1]||(vb[j+1]==0&&va[i]>j))++j;
if(j==m){
ans[++num]=i-m+1;j=nxt[j];
}
}
cout<<num<<"\n";
for(int i=1;i<=num;i++)cout<<ans[i]<<" ";
cout<<"\n";
}
似乎在梦中见过的样子
注意到
那么对于
这启发我们枚举后缀,求
注意到
位置相同但拆分不同的子串算同一子串
则对于一个位置找到了就马上break
void init(){
cin>>a+1>>k;n=strlen(a+1);
}
void get(int st){
tot=0;
for(int i=st;i<=n;i++)b[++tot]=a[i];
for(int i=2,j=0;i<=tot;i++){
while(j&&b[j+1]!=b[i])j=nxt[j];
if(b[j+1]==b[i])++j;
nxt[i]=j;
}
}
void solve(int tot){
for(int i=1;i<=tot;i++){
int m=nxt[i];
while(m>=k){
if(i-2*m>=1){
ans++;break;
}
m=nxt[m];
}
}
}
int main(){
init();
for(int i=1;i<=n;i++){
get(i);
solve(tot);
}
cout<<ans<<"\n";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!