字符串匹配(暴匹、 Rabin-Karp、KMP)

一、暴力匹配

贴上自己的代码,AC.

#include<stdio.h>
#include<string.h>

int main()
{
	char source[]="fiuhiuhrfeihgiuehjiuvnuifdhn";
	char pattern[]="hrfeih";
	
	int n=strlen(source);
	int m=strlen(pattern);
	
	
    printf("%d\n",n);
    printf("%d\n",m);
	int i,j;
	
	for(i=0;i<=n-m;i++){
		for(j=0;j<m;j++){
			if(pattern[j]!=source[i+j])
			   break;
		}
		if(m == j){
			printf("%d",i); 
			break;
		}	  
	}
	if(i==n-m+1)
	  printf("%d",-1); 
	return 0;
} 

二、Rabin-Karp算法

参考资料:http://www.ituring.com.cn/article/1759#

代码参考:http://blog.chinaunix.net/uid-26548237-id-3968132.html

三、KMP基础知识

KMP算法思想复杂,但实现却极其简单,已整理于笔记上。

参考资料:

http://chaoswork.com/blog/2011/06/14/kmp%E7%AE%97%E6%B3%95%E5%B0%8F%E7%BB%93/(最主要、最精彩的一篇)

http://blog.csdn.net/yutianzuijin/article/details/11954939

四、KMP应用

1. POJ2406:http://poj.org/problem?id=2406

    以模式串ababab为例

                        a          b         a          b      a      b       (虚位以待)

next:              – 1          0          0         1      2      3      4  (虚位以待

                       next[0]  next[1]  next[2]  ……               next[6] (虚位以待)

    注意:这里的next数组的值不同于KMP所用的next数组,区别在于:

                           a        b          a             b       a       b
KMP_next:      -1        0          0             1        2      3 

                     next[0]  next[1]  next[2]  ……              next[5] 

都是从next[1]开始计算,但是KMP_next不进行最后一个字符的比较工作(即真正有效的是前面5个字符,s[0]~s[4]),而next进行(全部的六个字符有效,s[0]~s[5]);最后一个字符之前的工作是完全一样的,显然可通过循环次数来控制这两种情况下next数组的获取(当i=4时,对于KMP_next来说,进行的是最后一次循环,此时比较s[4]与s[2],得到next[5]的值,(i=5)退出循环,但是next却仍有一次循环(因为此时i=5<6),此时比较s[5]与s[3],得到next[6]的值)。

    代码如下:

     

void KMP_Next(char* p,int next[])  
{  
    int pLen = strlen(p);  
    next[0] = -1;  
    int j = -1;  
    int i = 0;  
    while ( i < pLen - 1)    //注意KMP的next数组就在这里不同!
    {  
        if (j == -1 || p[i] == p[j])   
        {  
            ++i;  
            ++j;  
            next[i] = j;  
        }  
        else   
        {  
            j = next[j];  
        }  
    }  
}  

        回到本题,分析如下:strlen(dst) = 6 ,但前缀、后缀的最长相等长度却等于4,按理来说模式串的长度等于4 × 2 = 8 ,才会使得恰好不存在中间的循环串(此时恰好是2个相等的字符串重复出现),但现在总长度小于8,说明模式串中间是存在重复串的,这种情况下重复出现的字符串的个数肯定是大于2的,

循环节的长度:设strlen(dst) = n , 则循环节的长度 = n – next [ n ] 。原因:next[6] =4 , 必然有next [4] = 2,←←←←s[0 1 2 3] = s [2 3 4 5],则s[0 1] = s [2 3] ,显然next [4] = 2。如果继续往下推,则有s[4 5] = s [2 3],则循环节的长度为 s[4 5] 的长度2 ,也就是

《字符串匹配(暴匹、 Rabin-Karp、KMP)》

        重叠部分同时作为前缀的后半部分和后缀的前半部分。

        模式串长度除去最长相同前缀后缀长度,剩下的长度就是循环串的长度。

        除了上面的那种以strlen(dst) < =  2× 前缀、后缀的最长相等长度来判断是否存在循环串以外,还有一种方法:

n %  循环串的长度  ?  = 0 ,如果 = 0,说明存在循环串,循环串的个数等于 n /  循环串的长度;如果 != 0 ,说明在某些位置夹杂着其他不属于循环串的字符(这是显而易见的!)

代码如下:

#include<iostream>
#include<string.h>
using namespace std;
int next[1000005];
char s[1000005];
void getnext( )           //这样得到的next数组是符合预期的(不同于KMP_next数组),利用了字符串(也是next的下标)长度比最大(字符串)下标值多1的特性
{
    int i=0,j=-1;
    next[0]=-1;
    int len=strlen(s);
    while(i<len)
    {
        if( j==-1 || s[i]==s[j] )
        {
            i++;
            j++;
            next[i]=j;
        }
        else
            j=next[j];
    }
}
int main()
{
    while(scanf("%s",s)>0)
    {
        if(s[0]=='.')
            break;
        int len=strlen(s);
        getnext();
        if(len%(len-next[len])==0)
            printf("%d\n",len/(len-next[len]));
        else
            printf("1\n");
    }
    return 0;
}

参考资料:http://www.cnblogs.com/ziyi–caolu/archive/2013/01/01/2841708.html

                    http://www.cnblogs.com/coredux/archive/2012/08/13/2635987.html

2. POJ 1961:http://poj.org/problem?id=1961

        给你一个字符串,求这个字符串到第i个字符为止的循环节的次数。
比如aabaabaabaab,长度为12.到第二个a时,a出现2次,输出2.到第二个b时,aab出现了2次,输出2.到第三个b时,aab出现3次,输出3.到第四个b时,aab出现4次,输出4.

        上一题是输出一个字符串的循环节出现的次数,这个是到第i个字符为止,其实就是多了一层循环——把这个字符串遍历一次即可。

    

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
#define N 1000010

char s[N];
int nextval[N];
int len;

void getnext(const char *s)
{
	int i = 0, j = -1;
	next[0] = -1;
	while(i != len)
	{
		if(j == -1 || s[i] == s[j])
			next[++i] = ++j;
		else
			j = next[j];
	}
}

int main()
{
	int T = 1;
	int length, add;
	while(scanf("%d", &len) && len)
	{
		scanf("%s", s);
		getnext(s);
		printf("Test case #%d\n", T++);
		for(int i = 1; i <= len; ++i)
		{
			length = i - next[i];              //循环节的长度
			if(i != length && i % length == 0) //如果有多个循环串(不止1个)
				printf("%d %d\n", i, i / length);
		}
		printf("\n");
	}
	return 0;
}

参考资料:http://blog.csdn.net/niushuai666/article/details/6967716

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/LANGQING12345/article/details/42834185
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞