算法提升之字符串练习-03(KMP)

今天给大家带来的仍是关于字符串类型的算法题目,关于这类题目,大家需要多做练习进行巩固,题型相对固定,但是比较具有思路,希望大家可以好好理解相关部分。

关于KMP算法,通常有两部分组成,第一部分是通过get_next()数组求解next数组,第二部分则是通过KMP求解字符重复。

第一道题:

问题描述

wzy 给了你一个字符串,请你计算一下这个字符串最多是由多少个相同子串拼成的。

注意:原串 abcdabcd,则 abcd 出现两次,则该字符串最多是由两个相同子串拼成的。

输入格式

第一行一个字符串 s 。

输出格式

输出一个数,表示这个字符串最多是由多少个相同子串拼成的。

输入案例:

abcdabcd

输出案例:

2

代码解析:

#include 
using namespace std;
const int N=1e6+10;
char T[N],S[N];
int nex[N];
int n,n1;
void get_nex(int n){
  nex[0]=nex[1]=0;
  for(int i=2,j=0;i<=n;i++){
    while(j&&T[i]!=T[j+1])j=nex[j];
    if(T[i]==T[j+1])j++;
    nex[i]=j;
  }
}
int KMP(int T_length,int S_length){
  int ans=0;
  for(int i=1,j=0;i<=T_length;i++){
    while(j&&T[i]!=S[j+1])j=nex[j];
    if(T[i]==S[j+1])j++;
    if(j==S_length)ans++;
  }
  if(n%ans==0)return ans;
  else return 1;
}
int main()
{
  cin>>T+1;
  n=strlen(T+1);
  get_nex(n);
  n1=n-nex[n];
  for(int i=1;i<=n1;i++)S[i]=T[i];
  int b=KMP(n,n1);
  cout<

关键点⚠️:
1.最小重复字串长度的求法:

n1 = n - nex[n]
  • 原理
    对于整个字符串T[1..n]nex[n]是其最长前后缀的长度。若T是由某个子串重复组成的,则其最小重复子串的长度为n - nex[n]

    推导:假设T由子串t重复k次组成,那么T的最长前后缀长度nex[n]必然等于n - len(t)(因为前后缀会重叠k-1次)。因此,len(t) = n - nex[n]
  • 示例
    • T = "abcdabcd"n=8):
      nex[8] = 4(最长前后缀是"abcd"),则n1 = 8 - 4 = 4,即最小重复子串长度为 4("abcd")。
    • T = "ababab"n=6):
      nex[6] = 4(最长前后缀是"abab"),则n1 = 6 - 4 = 2,即最小重复子串长度为 2("ab")。
  • 合法性检查
    只有当n能被n1整除时(n % n1 == 0),n1才是有效最小重复子串长度;否则,字符串无法由重复子串组成,答案为 1。

第二题:

问题描述

在古代的文人墨客中,有一种名为“诗歌双联”的创作形式,它将两个诗句拼接在一起,构成具有深意的诗歌。现在,一位名为小桥的学者正在探索这种神秘的创作形式。她发现,通过选取两个不相交的子句,并将它们拼接在一起,有时可以创作出包含特定主题的诗歌。

小桥希望你能帮助她设计一个算法,验证她的想法。给定两个字符串 s 和 t,你需要判断是否可以从字符串 s 中选出两个长度为 k的不相交子字符串,拼接后得到的字符串包含字符串 t。

输入格式

第一行包含三个整数 n,m,k(2≤m≤2⋅k≤n≤102),分别代表字符串 s和字符串 t的长度,以及可选子字符串的长度。

接下来两行分别给出由小写字母组成的字符串 s 和 t。

输出格式

如果存在这样的两个子字符串,使得拼接后得到的字符串包含字符串 t,则输出 "Yes",否则输出 "No"。

输入案例:

7 4 3
baabaab
aaaa

输出案例:

Yes

代码部分:

#include
using namespace std;
const int N=1e4+5;
typedef unsigned long long ull;
char s[N],t[N];
const int base=131;
ull bases[N],h1[N],h2[N];
int n,m,k;
ull gethash(int l,int r,ull s[]){return s[r]-s[l-1]*bases[r-l+1];}
int main()
{  cin>>n>>m>>k;
  cin>>s+1>>t+1;
  bases[0]=1;
  for(int i=1;i<=n;i++)
  {
    bases[i]=bases[i-1]*base;
    h1[i]=h1[i-1]*base+(int)s[i];
    if(i<=m)
    h2[i]=h2[i-1]*base+(int)t[i];
  }
  for(int i=1;i+m-1<=n;i++)           //特判一下拆分t数组,一边把t数组全选的情况
  {
    if(gethash(i,i+m-1,h1)==gethash(1,m,h2))
    {
      cout<<"Yes"<<'\n';
      return 0;
    }
  }
  for(int i=1;ik||m-i>k)continue;
     int l=k,r=n-k+1;
     while(l

好了今天的分享就到这里了,希望大家可以多加巩固。

你可能感兴趣的:(算法,数据结构)