查看原题

题意大致是:给你一个字符串算这里面全部前缀出现的次数和。比方字符串abab,a出现2次。ab出现2次,aba出现1次。abab出现1次。总计6次。

而且结果太大。要求对1007进行模运算。

AC代码

#include <iostream>
using namespace std;
#include <string>
string s;
int n,Next[200005];
void getNext()
{
int len = n;
Next[0]=-1;
int i=0,j=-1;
while (i<len)
{
if(j==-1||s[i]==s[j])
{
++i;
++j;
Next[i]=j;
}
else
j = Next[j];
}
} void main()
{
int t;
cin>>t;
while (t--)
{
cin>>n;
cin>>s;
getNext();
int sum=0;
for(int i=1;i<=n;i++)
{
int j=i;
while(j)
{
sum = (sum+1)%10007;
j = Next[j];
}
}
cout<<sum<<endl;
}
}

KMP的next数组

概述

贴代码不是目的,解说算法才是关键。!

。解题的思路是使用了 KMP 算法,然而把并非完整的KMP算法。仅仅用到了它的next数组的求法。

然而这正是KMP算法本身的关键所在。关于上面代码中getNext函数中进行的求next数组的实现部分,属于经典实现。模板代码。

非常easy找到。这里关键在于解说next数组的思想。

在漫天飞的网络资料中,next数组的表示方法大致有两种:

  • next数组第一位为-1
  • next数组第一位为0

基本上是异曲同工。这里我用的是首元素为-1的解决方式,要注意的是若是首元素为-1的方案。那么next数组的大小是模式串长度+1!举个样例:

下标 0 1 2 3 4
模式串 a b a b  
next数组 -1 0 0 1 2

        当然了,这里我表示的是c++的string字符串。不是C风格字符串,所以没写'\0'.假设是C风格字符串(字符数组)那么红色部分就是'\0'了。只是这不是重点,不是么?

在KMP算法中,关于next数组一般也作两种理解(以next数组首元素为-1为例,为0时表述略有不同):

  1. 在模式串在某处与主串失配时,模式串应该回溯的位置。

  2. 以当前位置的前一位为结尾,其之前字符串与该串前缀相配的最大长度。

以下,略为解释一下这两点:

第一点

比方有一主串abacabab。有一模式串abab。要从主串之中查找是否包括模式串。那么我们依次遍历两个串,如果遍历两串有两个指针(逻辑意义上的指针)。或者称为光标。

開始时,前三位都能匹配。

下标 0 1 2 3 4 5 6 7
主串 a b a c a b a b
模式串 a b a b        

然后在下标为3处。也就是红色部分失配了。

那么朴素的字符串匹配算法就是要让主串的指针移动到下标为1.模式串指针归零,即移到首位。然而这非常明显是低效的操作。

KMP算法则是在这样的情况下。不改动主串的指针,仅仅改动模式串指针,故KMP算法又称无回溯KMP算法。那么模式串指针改动为什么呢,那就要看next数组了。

在上例中在下标为3处失配,则去看next[3],没错是 1 。

于是

下标 0 1 2 3 4 5 6 7
主串 a b a c a b a b
模式串     a b a b    

直接把模式串的指针移动到 下标为 1 处。再次失配,则观察 next[1] =0.继续反复上一过程。直至遍历完毕。KMP算法效率为O(m+n),当中m和n分别为主串和模式串的长度。

第二点

我们再次观察next数组的表格。

下标 0 1 2 3 4 5 6 7
主串 a b a c a b a b
模式串 a b a b        
  • 当下标为1时,要看它前一位的字符串,也就是看a,自身匹配不算。

    next数组为0。

  • 当下标为2时,要观察ab,a与b不匹配。next数组为0。
  • 当下标为3时,要观察aba,此时末尾的a与前缀a匹配,由于匹配长度为1所以next数组为1.
  • 当下标为4时。要观察abab,此时末尾的ab与前缀ab匹配,由于匹配长度为2所以next数组为2.

回到本题

代码中:

        int sum=0;
for(int i=1;i<=n;i++)
{
int j=i;
while(j)
{
sum = (sum+1)%10007;
j = Next[j];
}
}

用于求解全部前缀出现次数和。那么为什么这样呢?

首先看for循环,从1遍历到n,大家应该非常明确了。

我们的next数组的长度比串长度多1个。

while(j)造成的情况就是for循环中i = 1,2,3……n都会使sum+1.

这是非常好理解的由于,比方abab,那么 a。ab。aba,abab。这4个前缀肯定会算1个的对不?那么长度为n的字符串也会至少使sum+n对不。

然后接下来是 j = next[j].接下来我们用逆向思维来解说,另举一例。另有以字符串ababa,求它的sum(前缀出现次数和)。我们能够得到它的next数组:

下标 0 1 2 3 4 5
模式串 a b a b a  
next数组 -1 0 0 1 2 3

代入到上述代码中。和abab相比,仅仅多了一位。所以直接看 i 等于n(n为5)的时候。在sum=6(abab的sum值为6)的基础上来看。

  • j=i=5                //表示的是ababa这个长度为5的最长前缀
  • while(j)成立。sum=6+1=7
  • j=next[5]=3      //表示的是aba这个长度为3的前缀
  • while(j)成立,sum=7+1=8
  • j=next[3]=1      //表示的是a这个长度为1的最短前缀
  • while(j)成立。sum=8+1=9
  • j=next[1]=0.
  • while(j)不成立,结束。
  • 终于sum=9

要理解上面的凝视部分,须要再回到前面去看关于next数组解释的 第二点

最新文章

  1. 【javaweb学习】解析XML
  2. DeDeCMS 每次都被黑出翔了!!DEDECMS漏洞扫描
  3. docker 容器日志集中 ELK + filebeat
  4. php中curl远程调用获取数据
  5. [转]设置Jupyter-Notebook表格打印多个变量的值
  6. Extensions in UWP Community Toolkit - Mouse Cursor
  7. Oracle经常用到的一些函数
  8. matlab 将数字矩阵转换成图像
  9. python基础之Day9
  10. Jmeter安装与实例
  11. vue启动时报错,node-modules下xxx缺失
  12. (3)The critical role librarians play in the opioid crisis
  13. shell 中的$0 $1 $* $@ $# $$ $? $() $(())
  14. 【python】重定向输出
  15. c++入门笔记
  16. HBuilder android 打包指南(V客学院技术分享)
  17. integration asp.net web api with autofac and owin
  18. TunnelBroker for EdgeRouter 后记
  19. JavaWeb学习笔记(二十一)—— 监听器Listener
  20. MSSQL中循环

热门文章

  1. Usage of API documented as @since 1.6+
  2. poj2104(划分树模板)
  3. 线段树【SP1043】GSS1 - Can you answer these queries I
  4. Count of Smaller Numbers After Self -- LeetCode
  5. [BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)
  6. 【bzoj3261】【最大异或和】可持久化trie树+贪心
  7. 七. 多线程编程2.Java线程模型
  8. sqlserver 下载地址(SQL Server 2008 R2 中英文 开发版/企业版/标准版 下载)
  9. 【Linux】CentOS7上的一些操作小方法
  10. Java:网络编程之登陆服务器