kmp和字符串相关

 

Kmp

思想难代码简单的模范算法 贪心 dp搜索之类的算法被没有事先了解过的人灵机一动想到都可以理解 但没有接触过kmp算法的人想到这个算法就不太可能

 

把最常用算法之一:字符串匹配的复杂度降低数量级 kmp取自3个发明人的首字母

 

比如给出2个串 ab 求b在a中出现的次数(主串中出现了多少个模式串)

通常暴力一位一位的匹配 复杂度最坏是O(n*m)(随机字符串时常数很小 而且字符的种类越多常数越小)

但最坏的情况 aaaaaaaaaaaa中有几个aaaa 每一次匹配都成功 复杂度就是O(n*m)

或者ababababababababababab 中有几个ababb 总是匹配到最后一位才发现不同 复杂度也很高

 

可以发现 复杂度高的时候 都是因为模式串匹配了很长才跳出 而下次的匹配又从模式串头开始 有大量重复的判断(比如aaaaaaaaaaaa中有几个aaaa 每次模式串匹配完成 主串从下一位匹配 模式串从头匹配)

所以考虑 在模式串匹配了一段距离 匹配完成或发现不同时 怎样跳过不可能匹配成功或一定匹配成功的地方呢

比如在abcabcabc...中匹配abcabe 匹配到第6个字符发现不同 这是主串应该回到第二个字符 模式串从头匹配 但主串的前5个字符已经比对 得到了一些信息 怎样用这些信息呢

 

2个串的前5位相同第6位不同 观察前5位 abcab 显然主串只移动1位或2位都不能匹配 移动3位就可以匹配!正好是字符串的前缀和它的后缀一样的时候(前2位和后2位都是ab) 可以匹配

所以我们只要知道模式串中前缀和后缀的最大重合长度 就可以瞬间找到下一个可能的匹配 不用1位1位的位移 就可以有效利用匹配失败时的信息了

 

而被匹配串中前缀和后缀的最大重合长度只和模式串有关 只要预处理模式串就好(通常叫next数组 next[i]表示被匹配串前i位前缀与后缀的最大重合长度)

比如aaaaaaaaaaaaaa中找aaaaaa 被匹配串aaaaaa的公共缀显然是它的长度-1 所以每次匹配成功时 只用后移1位 比较主串

具体求法和用法看代码吧 都很短

int main()

{

    scanf("%*d");

    while (~scanf("%s%s", a, b))

    {//b中有几个a(可重叠)

        memset(ne, 0, sizeof(ne));

        x = strlen(a), y = strlen(b);

        j = 0;

        for (i = 1; i < x; i++)//O(x)

        {

            while (j > 0 && a[i] != a[j])j = ne[j];

            if (a[i] == a[j])j++;

            ne[i + 1] = j;//ne数组下标从1到x ne[i]表示前i个字符中最长公共缀长度

        }

        j = 0, z = 0;

        for (i = 0; i <= y; i++)//O(y)

        {

            while (j > 0 && b[i] != a[j]) j = ne[j];

            if (b[i] == a[j]) j++;

            if (j == x) z++, j = ne[j];

        }

        printf("%d\n", z);

    }

}

//现在觉得算法细节不用搞太懂 弄懂原理当做黑箱也好 最短路专题弄了好久 之后还是当做模板用...理解原理就好吧

 

循环节

根据next数组的定义 求一个字符串的循环节(如果有的话) 循环节长度是k=len-next[len]

如果n%k==0 则一定存在循环节 否则需要在字符串最后(或最前)加上缺的字符以构成一个完整的循环节 (hdu 3746)

所以根据next数组的求法 可以在O(n)的时间内求出长度为n的字符串的所有前缀的循环节(hdu 1358)

 

关于复杂度

Kmp匹配的复杂度是O(n+m)nm是2个字符串的长度 即和输入数据的大小成正比

所以只用做一遍kmp的题目(比如求匹配次数 是否匹配 循环节相关 公共缀相关)一定不会因为算法超时

 

字符串相关的trick

String很好用但容易超时 尤其是读入用cin太慢 怎么加速呢(最好G++提交)

scanf("%s", xx), ss[i] = xx;(其中ss[i]是string xx是char*)

Hdu6028 这么操作后kmp+string就不会MLE或TLE了

 

Map映射字符串也很好 但又涉及到string 怎么加速呢(当然用上面方法读string的话 map映射的速度都差不多)

字符串用char*做map的一种方法:(需要把作为索引的所有字符串都存下来)

struct cmp

{

    bool operator()(char *a, char *b) const

    {

        return strcmp(a, b) < 0;

    }

};

map<char *, char*, cmp>d;

 

↓↓重点↓↓

简单的说kmp算法最重要的几点是:next[i] 表示模式串长度为i的前缀的最大前后缀相同的长度

循环节长度k=len-next[len](必要不充分条件)

每句代码的功能及其必要性

 

 

 

 

Poj 3461&&hdu1686字符串匹配次数 模板

Hdu 1711 把char换成int 求第一次匹配下标

Hdu 2087 不能重叠的匹配次数 稍改一点点就好(暴力也能过..)

Hdu 3746 用到next数组的智力题..循环节相关 推荐

Hdu 1358 循环节和next数组的应用 推荐

Hdu 2594 求a的前缀和b的后缀最大重合长度 kmp处理一下就好(暴力也能过)

Hdu 3336 next数组应用的智力题

Hdu6028 kmp卡内存ac自动机卡时间的题(2017青岛网络赛晋级题)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值