zoukankan      html  css  js  c++  java
  • KMP算法习题(一本通)

    1465:【例题1】剪花布条

    kmp的典型例题

    因为找到了花布条不能重复,所以记得记录上一次找到的结束位置,当前位置减去结束位置大于需要的长度才算一个新的

    在每一次匹配中,这个串在S中的起点是i+1-m,终点是i

    1466:【例题2】Power Strings

    这个属于KMP解决最小循环节问题

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e6+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    //跟hash里面的一道题一模一样 
    
    //询问每个字符串最多是由多少个相同的子字符串重复连接而成的  求最短重复子序列
    //这道题试问我们一个串有几个循环节。循环节就是指相等的(真)前缀和(真)后缀的个数。我们知道,
    //kmp过程中的next[i]是这个意义:0-i-1位中相等的真前后缀个数。那么next[len]就是指0-len-1位中相等的真前后缀个数。
    char s[maxn];
    int next[maxn],len;
    void getnext(){
    	int j=-1;
    	int i=0;
    	next[0]=-1;
    	while(i<len){
    		if(j==-1||s[i]==s[j]){
    			i++;j++;
    			next[i]=j;
    		}
    		else j=next[j];
    	}
    }
    int main(){
    	while(1){
    		scanf("%s",s);
    		if(s[0]=='.') break;
    		memset(next,0,sizeof(next));
    		len=strlen(s);
    		getnext();
    		if(len%(len-next[len])==0) printf("%d
    ",len/(len-next[len]));
    		else printf("1
    ");
    	}
    return 0;
    }
    

      

    1467:Radio Transmission

    给你一个字符串,它是由某个字符串不断自我连接形成的。但是这个字符串是不确定的,现在只想知道它的最短长度是多少。

    KMP解决最小循环节问题

    https://blog.csdn.net/yyhs_wsf/article/details/84302201 看图理解

    最小循环节就是n-next[n]

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e6+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //处理循环节的问题
    //结论是: n-next[n]
    //看图好理解:https://www.sogou.com/link?url=hedJjaC291OB0PrGj_c3jHF3JVBxwgdKrV4_oh6vctquSdKpCMIsLs1-z3wO5pvEktsLTQmBnHo. 
    char s[maxn];
    int len,next[maxn];
    
    int main(){
    	scanf("%d %s",&len,s);
    	next[0]=-1;
    	int i=0,j=-1;
    	while(i<len){
    		if(j==-1||s[i]==s[j]){
    			next[++i]=++j;
    		}
    		else j=next[j];
    	}
    	printf("%d
    ",len-next[len]);
    return 0;
    }
    

      

    1468:OKR-Periods of Words

     

    KMP只能求出每个前缀串的最长匹配长度,如果要求出最短匹配长度,我们可以一直递推next[i],next[next[i]]...,直到为0. 熟悉的KMP本质的人都应该知道为什么,这里举一个例子。
    在S中,next[8]=5,而next[5]=2,next[2]=0了,所以next[5]=2就是8的最短匹配长度,将8-2累计到答案中即可。
    最后,类似求next时的递推方法,我们可以递推short来提高效率。比如在上例中,我们得到short[8]=2后,就直接将next[8]修改为2,
    这样8以后的数字如果递推到8了就可以直接跳到next[2]=0,而不用跳到next[5]这里。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e6+10; 
    const int INF=0x3fffffff;
    typedef long long LL;
    //这道题题目理解有点困难,可以画图理解
    //next[]数组求的是最大匹配前后缀,但是这道题根据题目的意思,想求的是最小匹配前后缀,才能使整个更长
    /*
    KMP只能求出每个前缀串的最长匹配长度,如果要求出最短匹配长度,我们可以一直递推next[i],next[next[i]]...,直到为0. 熟悉的KMP本质的人都应该知道为什么,这里举一个例子。
    在S中,next[8]=5,而next[5]=2,next[2]=0了,所以next[5]=2就是8的最短匹配长度,将8-2累计到答案中即可。
    最后,类似求next时的递推方法,我们可以递推short来提高效率。比如在上例中,我们得到short[8]=2后,就直接将next[8]修改为2,
    这样8以后的数字如果递推到8了就可以直接跳到next[2]=0,而不用跳到next[5]这里。
    */ 
    char s[maxn];
    int nxt[maxn];
    int len;
    unsigned long long ans=0;
    int main(){
    	scanf("%d",&len);
    	scanf("%s",s+1);
    	int j=0;
    	nxt[0]=nxt[1]=0;
    	for(int i=2;i<=len;i++){
    		while(j&&(s[i]!=s[j+1])) j=nxt[j];
    		j+=(s[i]==s[j+1]);
    		nxt[i]=j;
    	} 
    	for(int i=2;i<=len;i++){
    		if(nxt[nxt[i]]) nxt[i]=nxt[nxt[i]];
    	}
    	for(int i=2;i<=len;i++) if(nxt[i]) ans+=i-nxt[i]; 
    	printf("%lld
    ",ans);
    return 0;
    }
    

      

    1469:似乎在梦中见过的样子

     先理解一下暴力的解法,然后再开优化,这道题暴力好像过不了

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1.5e4+10; 
    const int INF=0x3fffffff;
    typedef long long LL;
    //暴力的做法,在一本通上面会超时 
    /*
    你就枚举一个左端点。。 
    然后对于这个左端点,我们对后面的字符串跑一次KMP 
    然后呢,再枚举一个右端点,判断行不行。。 
    其实呢就还是按KMP的next指针往前跳 
    然后你可以得到一段东西是可以的 
    然后,你还要保证中间有一个B的空间,于是要继续往前跳。。 
    跳完,你还要使这个区间>=k 
    于是就可以了。。 
    */ 
    int k,nxt[maxn];
    char s[maxn];
    int ans=0;
    void js(char *ss){
    	int j=0;
    	nxt[1]=0;
    	for(int i=2;ss[i];i++){
    		while(j&&ss[i]!=ss[j+1]) j=nxt[j];
    		if(ss[i]==ss[j+1]) j++;
    		nxt[i]=j;
    	}
    	j=nxt[2*k+1]-1;  //至少要保证这个长度 
    	for(int i=2;ss[i];i++){
    		while(j&&ss[i]!=ss[j+1]) j=nxt[j];
    		if(ss[i]==ss[j+1]) j++;
    		while(j*2>=i) j=nxt[j];  //不断往前走,保证存在B
    		if(j>=k) ans++;  
    	}
    } 
    int main(){
    	scanf("%s",s+1);
    	scanf("%d",&k);
    	for(int i=0;s[i+1];i++) js(s+i);  //必须要从0开始 
    	printf("%d
    ",ans);
    return 0;
    }
    
    
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=20000;
    const int INF=0x3fffffff;
    typedef long long LL;
    /*
    基本地:由于 O(n2) 就行,枚举子串左端点,计算每个子串的特征向量,对符合要求的累加。
    特别地:KMP中计算的特征向量 nxt[i] 表示的是以i为右端点,题目中A串的最小长度,由于题目对A的长度做了限制。于是,我们另开一个数组 f,
    记录满足A长度条件的最小长度 j,若 j 同时满足 2*j<i 则累加。
    收获:nxt数组本质上可以看作父节点表示法表示的一颗树,我们自根向下延申。要记录的是中间的满足条件的点。(之前最大循环节那道题 f 数组记录的就是根节点的值,
    不过我没意识到是棵树。。)
    */ 
    int k,n;
    LL ans=0;
    int nxt[maxn],f[maxn]; //,f记录满足A长度条件的最小长度 j,若 j 同时满足 2*j<i 则累加。
    char s[maxn];
    void get_nxt(int x){
    	nxt[1]=0;
    	int j=0;
    	for(int i=2;i<=n-x;i++){
    		while(j&&s[i+x]!=s[x+j+1]) j=nxt[j];
    		if(s[j+x+1]==s[i+x]) j++;
    		nxt[i]=j;
    		//f[]记录的其实是位置,能够满足next值大于k,去得到的最小的i的位置, 
    		if(f[j]) f[i]=f[j];
    		else if(j>=k) f[i]=j;
    		else f[i]=0;
    		if(f[i]&&(f[i]<<1)<i) ans++;
    	}
    	
    } 
    int main(){
    	scanf("%s %d",s+1,&k);
    	n=strlen(s+1);
    	for(int i=1;i<=n;i++) get_nxt(i-1);
    	printf("%lld
    ",ans);
    return 0;
    }
    

      

    1470:Censoring

     题解:

    https://www.luogu.com.cn/problemnew/solution/P3121

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    char s[100001],ori[100001];
    int n,tot,w,top;
    int trie[100001][26],fail[100001],heap[100001],sign[100001];
    int isend[100001];
    void insert(char *s){
        int now=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int x=s[i]-'a';
            if(!trie[now][x])trie[now][x]=++tot;
            now=trie[now][x];
        }
        isend[now]=len;
    }
    void makefail(){
        queue<int> q;
        for(int i=0;i<26;i++)
        if(trie[0][i])q.push(trie[0][i]);
        while(!q.empty()){
            int now=q.front();q.pop();
            for(int i=0;i<26;i++){
                if(!trie[now][i]){
                    trie[now][i]=trie[fail[now]][i];
                    continue;
                }        
                fail[trie[now][i]]=trie[fail[now]][i];
                q.push(trie[now][i]);
            }
        }
    }
    void solve(char *s){
        int now=0,len=strlen(s),i=0;
        w=0;
        while(i<len){
            int x=s[i]-'a';
            now=trie[now][x];
            sign[++top]=now;
            heap[top]=i;
            if(isend[now]){
                top-=isend[now];
                if(!top) now=0;
                else now=sign[top];
            }
            i++;
        }
    }
    int main()
    {
        scanf("%s",s);
        scanf("%d",&n);
        int len=strlen(s);
        for(int i=1;i<=n;i++){
            scanf("%s",ori);
            insert(ori);
        }
        makefail();
        solve(s);
        for(int i=1;i<=top;i++)
        printf("%c",s[heap[i]]);
        return 0;
    }
    

      

  • 相关阅读:
    周总结5
    《梦段代码》阅读笔记01
    NABCD
    结对开发(四则运算)
    《人月神话》阅读笔记03
    周总结4
    移动端疫情显示
    周总结3
    《人月神话》阅读笔记02
    软件工程第四周作业
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12726511.html
Copyright © 2011-2022 走看看