字符串刷题

刷题总结:
[kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher [Cloned]
A .KMP模板题
  • 注意要用int 数组存文本串和模式串。
  • 注意KMP算法的一些细节处理。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MA=1e6+5;
int p[MA];
int m[MA];
int n,w;
ll Next[MA];
void get_next()
{
    int i=0;
    int j=-1;
    Next[0]=-1;
    while(i<w){
        if(j==-1||m[i]==m[j]){
            i++;
            j++;
            Next[i]=j;
        }
        else j=Next[j];
    }
}
void KMP()
{
    get_next();
    int i=0;
    int j=0;
    while(i<n&&j<w){
        if(j==-1||p[i]==m[j]){
            i++;
            j++;
        }
        else j=Next[j];
    }
    if(j==w) cout<<i-w+1<<endl;
    else  cout<<"-1"<<endl;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&w);
        for(int i=0;i<n;++i)scanf("%d",&p[i]);
        for(int i=0;i<w;++i)scanf("%d",&m[i]);
        memset(Next,0,sizeof(Next));
        KMP();
    }
    return 0;
}

B. KMP水题
  • 换为字符串数组
C. KMP模板题
  • 注意匹配位置不能重复(换一种写法)
int KMP()
{
    get_next();
    int i=0;
    int j=0;
    int ans=0;
    while(i<len_p){
        while(j!=-1&&p[i]!=m[j])j=Next[j];
        i++;
        j++;
        if(j==len_m){
            ans++;
            j=0;//每次清空匹配情况。
        }
    }
    return ans;
}
D. KMP找循环节

参考博客
题意: 给一段字符串,让你添加一些字符,使得字符串可以看作是由一段字符串不断重复得到的。问添加字符数量最小是多少?
分析:

  • 很明显是循环节问题,也就想到KMP算法求循环节,这好像是KMP的一个应用。
  • 用 get_next() 得到 next[ ] 数组,保存[ 0 ~ i-1]中最长公共前后缀长度。
  • i - next [ i ] 为最小循环节长度,如果i %(i - next[ i ])==0说明字符串是循环的,不需要添加字符串,但是我们要注意如果next[ i ] = 0的情况呢(也就是类似 absdwe 这种字符串,如果直接按公式 i % ( i - next [ i ] ) 计算结果是0,但这并不是循环)所以我们要加上限制条件(next [ i ]!=0)
  • [ 0 ~ i ]的字符串:
  • 如果满足 i % ( i - next[i] ) == 0 && next[i] != 0,就说明字符串循环
  • 计算最小循环节长度:len = i - next [ i ]
  • 计算最小循环节重复次数: i / ( i - next [ i ] )
  • 需要增加的个数为(好理解版 ):最小循环节长度减去总长度对最小循环节长度取余剩下的长度之差 (详见下面博客和图片 ) : ( len - len_m%len )%len
  • 需要增加个数为 len - Next[ len_m ]%len ( len 就是最小循环节长度 ),
    看懂增加个数的博客: https://blog.csdn.net/jadeyansir/article/details/77275203
    在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MA=1e6+5;
char p[MA];
char m[MA];
int len_p,len_m;
ll Next[MA];
void get_next()
{
    int i=0;
    int j=-1;
    Next[0]=-1;
    while(i<=len_m){
        if(j==-1||m[i]==m[j]){
            i++;
            j++;
            Next[i]=j;
        }
        else j=Next[j];
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",&m);
        len_m=strlen(m);
        memset(Next,0,sizeof(Next));
        get_next();
        int length=len_m-Next[len_m];
        if(len_m%length==0&&Next[len_m])cout<<"0"<<endl;
        else cout<<length-Next[len_m]%length<<endl;
    }
    return 0;
}

E . 循环节水题
  • 如上题,求next[ ] 遍历字符串,如果[ 0 ~ i ] 有循环节,就输出 i 和 循环节重复次数。
    //部分代码
    while(scanf("%d",&n)&&n){
      num++;
      scanf("%s",&s);
      len_m=strlen(s);
      get_next();
      printf("Test case #%d\n",num);
      for(int i=1;i<=len_m;++i){
        if(i%(i-net[i])==0&&net[i]!=0){
            printf("%d %d\n",i,i/(i-net[i]));
        }
      }
      cout<<endl;
    }
F .POJ 2406

AC通道

  • 题意:找最小循环节的重复次数。
  • 分析:
  • 如果字符串长度对最小循环节长度取模为0,就是整除。如ab ab ab ab可以被 ab 长度上整除。并且满足字符串一定有相等的前后缀(next[ len_m ] > 0)那么就输出字符串长度除以最小循环节长度。其他情况( abcde , abcabcab )都只能由一个字符串重复一次得到。
  • 如果len_m % ( len - next[ len_m ] ) == 0 且next[ len_m ]>0 就输出 len_m / ( len - next[ len_m ])。其他情况输出 1。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int MA=1e6*5+5;
char m[MA];
int len_m;
int net[MA];
void get_next()
{
    int i=0;
    int j=-1;
    net[0]=-1;
    while(i<=len_m){
        if(j==-1||m[i]==m[j]){
            i++;
            j++;
            net[i]=j;
        }
        else j=net[j];
    }
}
int main()
{
  while(scanf("%s",&m)){
    if(m[0]=='.')break;
    len_m=strlen(m);
    memset(net,0,sizeof(net));
    get_next();
    int length=len_m-net[len_m];
    if(len_m%length==0&&net[len_m])cout<<len_m/length<<endl;
    else cout<<"1"<<endl;
  }
  return 0;
}

g
h.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值