hihoCoder 1015-KMP算法

hihoCoder 1015-KMP算法 #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,表示模式串在原串中出现的次数。 样例输入 5 HA HAHAHA WQN WQN ADA ADADADA BABABB BABABABABABABABABB DAD ADDAADAADDAAADAAD 样例输出 3 1 3 1


题目大意就是用KMP算法求解一个子串在主串中出现的次数。我们都知道常规KMP算法可以用来判断一个模式串t是否在主串s中出现过,以及出现的位置。但是这题要求我们求出出现的次数。我们只需要对KMP算法稍加修改即可。 KMP的详细介绍可以看这里,我是仔细看了学校发的李春葆版本《数据结构》P105,代码也基本上照搬课本,使用的是修正后的求nextval数组的KMP算法。具体代码如下: [cpp] #include<iostream> #include<string> using namespace std; int nextval[10001]; //改进的next数组求法 void get_nextval(const string &t) { int j=0,k=-1; nextval[0]=-1; int t_len=t.size(); while(j<t_len) { //t[k]表示前缀,t[j]表示后缀 if(k==-1||t[j]==t[k]) { j++; k++; //较之前next数组求法,改动在下面4行 if(t[j]!=t[k]) nextval[j]=k; //之前只有这一行 else nextval[j]=nextval[k]; // 继续递归 } else k=nextval[k]; // 递归往前找 } } //t为模式串,s为主串;返回t在s中出现的次数 int get_show_times_kmp(const string &t,const string &s) { int t_len=t.size(),s_len=s.size(),i=0,j=0; int times=0; //int *nextval=new int[t_len]; get_nextval(t); while(i<s_len) { if(j==-1||s[i]==t[j]) { i++; j++; } else j=nextval[j]; if(j==t_len) { times++; //i=i-t_len+1; //j=0;//如果匹配成功的话,无需从t的开头重新匹配,也是只要将t右滑j-k个位置。看《数据结构》P107 } } return times; } int main() { int n; string t,s;//t为模式串,s为主串 cin>>n; while(n–) { cin>>t>>s; cout<<get_show_times_kmp(t,s)<<endl; } return 0; } [/cpp] 我做的修改只有一处,就是KMP搜索的时候,如果j==t_len说明在主串s中找到了一个t的匹配,令times++,此时程序还不能结束,我们要让匹配继续下去,为了让while成立,修改while条件只有i<s_len。 其实这个思路是我后来看某大神的解法思考过后才想明白的。我最开始的想法是这样的:既然在t.j和s.i处匹配成功,要进行下一轮匹配时,就应该令j=0,i=i-t_len+1;,也就是从主串s匹配成功起始位置的下一个位置和模式串t的起始位置开始重新匹配。这样做虽然正确,但是TLE超时。后来我仔细看课本P107图4.10,假设$$t=t_0t_j$$ ,且$$t_j==s_i$$,因为$$t_0…t_{k-1}==t_{j-k}…t_{j-1}$$,所以如果要进行下一次匹配,也可以直接将$$t_0…t_{k-1}$$右滑到$$t_{j-k}…t_{j-1}$$的位置,也就是$$t_0…t_{k-1}==s_{i-k}…s_{i-1}$$,再依次判断$$s_i$$是否等于$$t_k$$。这其实就是令j=nextval[j]。所以当j==t_len的时候,不需要特别处理,直接下一次循环就好。这样可以节省很多时间。 以上代码在hiho中AC,用时226ms,内存7MB。]]>

Leave a Reply

Your email address will not be published. Required fields are marked *