KMP算法 2203 亲和串

本文深入讲解KMP算法的核心思想及应用实例,详细解释next数组的计算方法及其在字符串匹配中的作用。

kmp算法

背景:已知字符串a和字符串b,求字符串b在字符串a中首次出现的位置

kmp算法的核心精髓在于求next数组

next数组含义:next[i] 以 i 结尾(不包括 i)的字符串中前缀和后缀最长相等的长度

比如:s = aabcdaabcd

前缀:a、aa、aab、aabc、aabcd ....... aabcdaabc 都是 s 的前缀

后缀:d、cd、bcd、abcd、aabcd ....... aabcdaabc 都是 s 的后缀

next 数组中 next[0] = -1 这个是一定的,可以看做是规定吧

s的next 数组为

i           0  1  2  3  4  5  6  7  8  9

s          a  a  b  c  d  a  a  b  c  d  

next    -1  0  1  0  0  0  1  2  3  4

就比如说 next[8] 的求法:

以8结尾的字符串为 aabcdaab,我们一眼就可以看出前缀和后缀的最长相等长度为3,即aab和aab,如何让计算机自己求呢?

求next[8]的时候,next[0]~next[7]都是已知的,那么我们可以利用next[7]的结果

以7结尾的字符串为aabcdaa,next[7]=2,那么求next[8]时可以直接判断s[7]是否等于s[next[2]]就好了,如果相等,那么next[8]=next[7]+1,如果不相等,那么就要在判断s[7]是否等于s[next[2]],继续下去,直到相等或者到next[0]=-1了,就跳出循环。

这个表达的也许不是很清楚,光看也不可能看懂,也许还是会有一些不动,一边动手,一边摸索明白可以更好地理解。总之一定要记得next数组的含义

求得了next数组,接下来就是要匹配了

比如 a = aabcdaabcd  i=0,指向a字符串

        b = cdaa              j=0,指向b字符串

先是一位一位的判断是否相等,如果相等那么i++、j++

                                                         不相等那么j = next[j],继续判断,为什么可以这样呢,因为在第 j 位失配(没有匹配),那说明字符串b从0~j-1和字符串a从i-j~i-1相等,而next[j]=k表示字符串b中0~k-1 和 j-k~j-1 是相等的,那么字符串a从i-j~i-1和0~k-1是相等的,即只要比较a[i]==b[next[j]]是否为true就好

KMP算法的思路就是这样了


思路明白了,多写几道题吧,边写边熟悉,直到自己会了

hdu 2203一道挺水的题,适合学会了KMP算法来写,基本上就是套模板了

亲和串


Problem Description
人随着岁数的增长是越大越聪明还是越大越笨,这是一个值得全世界科学家思考的问题,同样的问题Eddy也一直在思考,因为他在很小的时候就知道亲和串如何判断了,但是发现,现在长大了却不知道怎么去判断亲和串了,于是他只好又再一次来请教聪明且乐于助人的你来解决这个问题。
亲和串的定义是这样的:给定两个字符串s1和s2,如果能通过s1循环移位,使s2包含在s1中,那么我们就说s2 是s1的亲和串。
 

Input
本题有多组测试数据,每组数据的第一行包含输入字符串s1,第二行包含输入字符串s2,s1与s2的长度均小于100000。
 

Output
如果s2是s1的亲和串,则输出"yes",反之,输出"no"。每组测试的输出占一行。
 

Sample Input
AABCD CDAA ASD ASDF
 

Sample Output
yes no
 
题目为中文,看一眼就明白题目的意思,思路就是把s1+=s1,然后用kmp算法的模板就ok了

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn = 100000+5;
int Next[maxn];
void getNext(string s)
{
    Next[0]=-1;
    Next[1]=0;
    int ls=s.size();
    for(int i=2;i<ls;i++){
        if(s[i-1]==s[Next[i-1]]) Next[i]=Next[i-1]+1;
        else{
            int t=Next[i-1];
            while(s[t]!=s[i-1]){
                t=Next[t];
                if(t==-1) break;
            }
            Next[i]=t+1;
        }
    }
}
int kmp(string s1,string s2)
{
    int ls1=s1.size();
    int ls2=s2.size();
    int i=0,j=0;
    while(i<ls1 && j<ls2){
        if(s1[i]==s2[j]) i++,j++;
        else{
            //j=Next[j];
            while(s1[i]!=s2[j]){
                j=Next[j];
                if(j==-1){
                    i++;
                    j++;
                    break;
                }
            }
        }
        if(j==ls2) return i-ls2;
    }
    return -1;
}
int main()
{
    string s1,s2;
    int ans;
    while(cin>>s1>>s2){
        getNext(s2);
        //for(int i=0;i<s2.size();i++) cout<<Next[i]<<' ';cout<<endl;
        s1+=s1;
        ans=kmp(s1,s2);
        //cout<<"ans="<<ans<<endl;
        if(ans==-1) cout<<"no"<<endl;
        else cout<<"yes"<<endl;
    }
    return 0;
}
</span>


下载前必看:https://pan.quark.cn/s/a4b39357ea24 在本资料中,将阐述如何运用JavaScript达成单击下拉列表框选定选项后即时转向对应页面的功能。 此种技术适用于网页布局中用户需迅速选取并转向不同页面的情形,诸如网站导航栏或内容目录等场景。 达成此功能,能够显著改善用户交互体验,精简用户的操作流程。 我们须熟悉HTML里的`<select>`组件,该组件用于构建一个选择列表。 用户可从中选定一项,并可引发一个事件来响应用户的这一选择动作。 在本次实例中,我们借助`onchange`事件监听器来实现当用户在下拉列表框中选定某个选项时,页面能自动转向该选项关联的链接地址。 JavaScript里的`window.location`属性旨在获取或设定浏览器当前载入页面的网址,通过变更该属性的值,能够实现页面的转向。 在本次实例的实现方案里,运用了`eval()`函数来动态执行字符表达式,这在现代的JavaScript开发实践中通常不被推荐使用,因为它可能诱发安全问题及难以排错的错误。 然而,为了本例的简化展示,我们暂时搁置这一问题,因为在更复杂的实际应用中,可选用其他方法,例如ES6中的模板字符或其他函数来安全地构建和执行字符。 具体到本例的代码实现,`MM_jumpMenu`函数负责处理转向逻辑。 它接收三个参数:`targ`、`selObj`和`restore`。 其中`targ`代表要转向的页面,`selObj`是触发事件的下拉列表框对象,`restore`是标志位,用以指示是否需在转向后将下拉列表框的选项恢复至默认的提示项。 函数的实现通过获取`selObj`中当前选定的`selectedIndex`对应的`value`属性值,并将其赋予`...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值