HDU 6153 KMP

HDU6153:http://acm.hdu.edu.cn/showproblem.php?pid=6153

Orz 果然字符串很神奇……(神奇到人哭出来啊TAT)果然算法理解的不够透彻的话,是无法灵活运用的QAQ

其实只要简单的改一点点KMP的地方……

KMP的next[i]存储的是模式串下标0至i处相同的前缀和后缀的最长长度,每次与目标串匹配的时候,会略过前后缀相同的部分以减少回溯,提高效率。

由于每次匹配都是从前向后匹配的,因此对于模式串每次的匹配子串都是[0……i],是前缀,但是根据题目的要求,我们要找的是s2的子串[i……n],是后缀。

其实只要把模式串reverse一下,就可以把后缀转化为前缀,然后在进行处理就方便了。

对kmp理解的比较透彻的就不要听我瞎扯了……写的比较……emmmmm……不过还是很欢迎给窝这个连门槛都摸不到的小白提提建议的(^?^*)

可以用一个ll数组记录长度为i的前缀(也就是转化前的后缀)出现的次数(这里数组用crt表示)。(用ll……不要用int……会TLE)

因为next数组本身记录的就是相同子串的长度,因此可以在每个循环内部对匹配过的字符的下标进行计数

先码个代码:

while (i < len1)
    {
        if(j==-1||s1[i]==s2[j])
          {  i++; j++;   }
        else j = nextt[j];
        num[j]++;
        if (j == len2) j = nextt[j];
    }

下面拿题目的例子来解释一下出现次数的计数:

b     a    b    a    b    a    b    a

next      -1    0    0    1    0    0    0    0

a                                                        ——模式串目标串匹配过程

crt        0     0    0     0       匹配失败,j=next[j]=-1;这里crt数组不更新

crt        1     0    0     0

a

crt       1      1    0     0

a    b

crt       1      1     1

a    b     a                  ——j==s2长度,但由于我们要在整个目标串中匹配,这里不能跳出,要继续匹配

crt       1     1    1     1                                      因此又加了  if(j==len2)  j=next[j];

a    b

crt       1     1     2    1

a    b    a

crt       1     1     2    2

…………

a   b    a

crt      1     1    3    3

可以发现,把crt计数放在每次j处理过之后,下标刚好就为目前子串的长度,所以就可以安心的记录啦;

前面有提到,kmp的算法为了减少回溯,省略了相同部分的匹配,因此我们每次记录的只是当前相同前缀长度的个数,从上面的例子不难发现,

除了第一次‘a‘以外的其他的‘a‘的匹配都被省略了,其他都是从’ab‘开始匹配的,每次匹配失败再次匹配时,省略了next[i]之前部分的计数,因此还需要进一步对计数数组进行处理。

for (int i = len2; i >0; i--)
       crt[nextt[i]] += crt[i];

既然少了加回来就好了,匹配失败以后再次匹配时,都是从next[i]开始匹配的,因此,crt[next[i]]未计数的部分=crt[i],循环把len~1的部分加回来就OK了

(记得是从len倒着往前加)

下面看完整的代码。

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX = 1e6;
const int mod = 1e9 + 7;
int nextt[MAX + 10];
char s1[MAX + 10];
char s2[MAX + 10];
long long num[MAX + 10];
int t,len1,len2;
void getnext()
{
    int p=0, k = -1;
    nextt[0] = -1;
    while (p < len2)
    {
        if (k == -1 || s2[p] == s2[k])
        {
                    p++;k++;
                     nextt[p] =k;
                }
        else k = nextt[k];
    }
}
void kmp()
{
    getnext();
    int i = 0, j = 0;
    while (i < len1)
    {
        if(j==-1||s1[i]==s2[j])
        {i++; j++;}
        else j = nextt[j];
        num[j]++;
        if (j == len2) j = nextt[j];
    }
}
int main()
{
    cin >> t;
    while (t--)
    {
        memset(num,0, sizeof(num));
        cin >> s1;
        cin >> s2;
        len1 = strlen(s1);
        len2 = strlen(s2);
        reverse(s1, s1 + len1);
        reverse(s2, s2 + len2);
        kmp();
        long long ans = 0;
        for (int i = len2; i > 0; i--)
        {
            num[nextt[i]] += num[i];
            ans = (ans + i*num[i]) % mod;
        }
            printf("%d\n", ans);
    }
    return 0;
}

有什么不对的地方或者解释的不好的地方还请大家指出来( ̄▽ ̄)"

时间: 2024-08-28 15:38:47

HDU 6153 KMP的相关文章

HDU 6153 A Secret(扩展KMP模板题)

A Secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others) Total Submission(s): 2523    Accepted Submission(s): 934 Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,w

hdu 1711 KMP模板题

// hdu 1711 KMP模板题 // 贴个KMP模板吧~~~ #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int MAX_N = 1000008; const int MAX_M = 10008; int T[MAX_N]; int p[MAX_M]; int f[MAX_M]; int

hdu 1686 KMP模板

1 // hdu 1686 KMP模板 2 3 // 没啥好说的,KMP裸题,这里是MP模板 4 5 #include <cstdio> 6 #include <iostream> 7 #include <cstring> 8 #include <algorithm> 9 10 using namespace std; 11 12 const int MAX_N = 1000008; 13 const int MAX_M = 10008; 14 char T

Cyclic Nacklace HDU 3746 KMP 循环节

Cyclic Nacklace HDU 3746 KMP 循环节 题意 给你一个字符串,然后在字符串的末尾添加最少的字符,使这个字符串经过首尾链接后是一个由循环节构成的环. 解题思路 next[len]-len的差即是循环部分的长度. 这个是重点.这个题目自己开始没有想明白,看的博客,推荐这个. 代码实现 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const i

【扩展kmp】HDU 6153 A Secret

acm.hdu.edu.cn/showproblem.php?pid=6153 [题意] 给定字符串A和B,求B的所有后缀在A中出现次数与其长度的乘积之和 A和B的长度最大为1e6 [思路] 把A和B同时反转,相当于求B的所有前缀在A中出现次数与其长度的乘积之和 换个角度,相当于A中每出现一个B的前缀,答案中就要加上该前缀的长度 考虑A中每个位置对答案的贡献,A[i...lenA-1]与B的最长公共前缀是x,则B中的前缀B[0...1],B[0....2]...B[0....x]都在A中出现,那

2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6153 A Secret KMP,思维

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6153 题意:给了串s和t,要求每个t的后缀在在s中的出现次数,然后每个次数乘上对应长度求和. 解法:关键在于想到把s和t都翻转之后,把t求next,然后用t去匹配s,在匹配过程中把fail指针跳到的地方加1,但是还没完,最后需要反向遍历第二个串将大串对小串的贡献加上去就可以了. 这道题是很多现场AC的代码是有漏洞的,比如bazbaba,bazbaba这个答案是34,但是很多现场AC的代码会输出31.

HDU 6153 A Secret(扩展kmp)

A Secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others)Total Submission(s): 1530    Accepted Submission(s): 570 Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,wh

HDU 6153 拓展KMP (2017CCPC)

A Secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others)Total Submission(s): 975    Accepted Submission(s): 372 Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,whi

HDU 6153 A Secret (KMP)

题意:给定两个串,求其中一个串 s 的每个后缀在另一个串 t 中出现的次数. 析:首先先把两个串进行反转,这样后缀就成了前缀.然后求出 s 的失配函数,然后在 t 上跑一遍,如果发现不匹配了或者是已经完全匹配了,要计算,前面出现了多少个串的长度匹配也就是 1 + 2 + 3 + .... + j 在 j 处失配,然后再进行失配处理.注意别忘了,匹配完还要再加上最后的. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000"