ACM #1015 KMP算法

来源:互联网 发布:美丽折淘客软件下载 编辑:程序博客网 时间:2024/06/09 13:26

#1015 : KMP算法

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。

这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?

小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就肯定不会这么容易的让他们回答了,于是他们只能说道:“抱歉,河蟹先生,我们只能想到时间复杂度为(文本长度 * 特殊文字总长度)的方法,即对于每个模式串分开判断,然后依次枚举起始位置并检查是否能够匹配,但是这不是您想要的方法是吧?”

河蟹点了点头,说道:”看来你们的水平还有待提高,这样吧,如果我说只有一个特殊文字,你能不能做到呢?“

小Ho这时候还有点晕晕乎乎的,但是小Hi很快开口道:”我知道!这就是一个很经典的模式匹配问题!可以使用KMP算法进行求解!“

河蟹满意的点了点头,对小Hi说道:”既然你知道就好办了,你去把小Ho教会,下周我有重要的任务交给你们!“

”保证完成任务!”小Hi点头道。

提示一:KMP的思路

提示二:NEXT数组的使用

提示三:如何求解NEXT数组

输入

第一行一个整数N,表示测试数据组数。

接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。

其中N<=20

输出

对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。



题目很明确,就是编写KMP算法。那就先来解释一下KPM算法的思路。

有两个字符串,目标字符串W和模式字符串T。当W和T失配后,并不是简单地从目标串下一个字符开始新一轮的检测,而是依据模式字符串的next数组,从字符串T的某个位置直接继续匹配。这样就提高了效率。

next数组的作用:指导下一次该匹配的位置。

比如字符串T为bbsbbc,当在字符c处失配则下一次匹配可以从s字符串开始,因为c前面的字符bb和s前面的字符bb是相同的,所以可以直接到s。当在s处失配的话,下一次可以从第二个b开始,因为b前面肯定是b,前面已经匹配过了。如此我们可以发现规律,在哪里发生失配,只要比较它前面的字符串中前缀和后缀的相同个数,从而求出next数组。那么如何计算next数组?

设next[0]为-1,接着看j=1,T[1]=b前面只有一个,所以肯定是0。j=2,T[2]=s。前缀是b,后缀也是b,有一个相等,所以next[2]=1。j=3,T[3]=b。前面是bbs,前缀和后缀都不相等,所以next[3]=0。以此类推,可以总结为前缀就是从字符串初始位置开看,后缀到当前字符串为止去看。前缀后缀有多少个字符串相等,next[j]就等于多少。下面来看代码:

//计算next数组,作用是指导下一次该匹配的位置void getNext(string T, int *next){next[0] = -1;//第一位就不匹配,则返回-1int i = 0, j = -1;int str_size = T.size();while (i < str_size){//i是后缀,j是前缀if (j == -1 || T[i] == T[j]){i++;j++;next[i] = j;} else {j = next[j];//j回溯,后缀是相对的。}}}
选一个字符串对着代码推一遍应该就能懂的。

搞懂了next数组,匹配就很简单了。不多说,直接看代码吧

//匹配算法。//思路:当目标字符串与模式字符串不匹配时,根据next数组将模式字符串回朔到某个位置,而不是从头开始匹配int matchString(string T, string W, int *next){int i = 0, j = 0;int sum = 0;int T_size = T.size();int W_size = W.size();while(i < W_size){if (W[i] == T[j]){//模式字符串全部相同时,说明目标串中有相同字符串if (j == (T_size - 1)){sum++;j = 0;}i++;j++;} else {//在不匹配位置,根据next数组将模式字符串回溯到已经匹配过的地方j = next[j];}//如果第一个就不匹配,则都+1if (j == -1){i++;j++;}}return sum;}int main(){int n = 0;//表示测试数据组数。cin >> n;string T;//模式字符串string W;//目标字符串int next[255];int out[255];for(int i = 0; i < n; i++){cin >> T;cin >> W;getNext(T, next); int sum = matchString(T, W, next); out[i] = sum;}for (int i = 0; i < n; i++){cout << out[i] <<endl;}return 0;}







0 0
原创粉丝点击