KMP算法

//BF算法
/*对于此算法,也是最常规的做法,同时遍历主串和模式串,判断字符相等则同时向后继续判断是否匹配
当不匹配时,主串从刚才开始的位置的下一个位置开始向后继续判断,而模式串则从最开始位置,
同此时的主串一起同时向后判断遍历判断,重复此过程
对于一定量的效率可行
当数据量达到一定量时,会花费很长很长时间取等待结果
*/

#include<bits/stdc++.h>
#define N 1001
using namespace std;
int Index(char s[],char t[])
{
	int s1=strlen(s);
	int t1=strlen(t);
	int i=0,j=0;
	while(i<s1&&j<t1){
		if(s[i]==t[j]){//继续匹配下一个字符 
			i++;
			j++;
		}
		else{//主串,子串指针回溯重新开始下一次匹配 
			i=i-j+1;//主串从下一个位置开始匹配 
			j=0;//子串从头开始匹配 
		}
	}
	if(j>=t1){
		return (i-t1);//返回匹配的第一个字符的下标 
	}
	else{
		return -1;//匹配不成功 
	}
}
int main()
{
	char a[N];
	cout<<"请输入主串:";
	gets(a);
	char b[N];
	cout<<"请输入模式串:";
	gets(b);
	int t=Index(a,b);
		cout<<t<<endl; 
	return 0; 
}

//KMP算法
/*KMP由三位大牛的名字命名,这个算法可以将算法的效率大大提高!对于大数据的效率远高于前面的算法!
此算法的思想是:没必要去每次重新判断子串,也莫必要去每次拉回主串
对于模式串,通过计算模式串的前缀和后缀相等的个数,来确定每次应该从模式串的那个位置去判断对应的主串的元素
用一个next数组来存储这些信息,next数组遵循一个固定的规律:next数组第一个元素总是-1(其值可以为0,但为了统一操作),
其余元素则为前缀与后缀数相等的个数-1;
以此规律来匹配主串与模式串,通过对应的next数组值来更改下标,达到减少匹配次数。
从而大大减少时间。
*/

#include<bits/stdc++.h>
#define NotFound -1
void BuildNext(char *pattern,int *next)
{
	int i,j;
	int m=strlen(pattern);
	next[0]=-1;//对于next数组,第一个元素没有与其相等的前缀,所以为-1,(可以为0,为了统一操作,取-1) 
	for(j=1;j<m;j++){
		i=next[j-1];//i表示前缀和后缀相等的字符个数-1,即下次匹配可以直接定位到模式串的下标 
		while((i>=0)&&(pattern[i+1]!=pattern[j])){//前面有匹配的,但当前前缀与后缀不匹配 
			i=next[i];//i往回缩 
		}
		if(pattern[i+1]==pattern[j]){//前缀和后缀匹配 
			next[j]=i+1;//将当前i+1赋值给next数组 
		}
		else{//前缀后缀不匹配 
			next[j]=-1;
		}
	} 
} 
int KMP(char *string,char *pattern)
{
	int n=strlen(string);
	int m=strlen(pattern);
	int s,p,*next;
	if(n<m){//模式串比主串长,直接返回,模式匹配失败 
		return NotFound;
	}
	next=(int *)malloc(m*sizeof(int));//next数组分配单元
	BuildNext(pattern,next);//得到next数组
	s=p=0;
	while(s<n&&p<m){
		if(string[s]==pattern[p]){//字符匹配,指针同时后移,继续匹配 
			s++;
			p++;
		}
		else if(p>0){//将模式串指针拉回到可以跳过匹配的字符下标,+1是为了和next数组统一 
			p=next[p-1]+1;
		} 
		else{//第一个字符都不匹配,主串继续向后遍历匹配 
			s++;
		}
	} 
	return p==m?(s-m):NotFound;//跳出循环s和p相等,表示找到了,s-m即为初始位置下标 
}
int main()
{
	char string[]="aaaaaa bbbbaaabbbccc";
	char pattern[]="aaabbb";
	int p=KMP(string,pattern);
	if(p==NotFound){
		printf("Not Found!");
	}
	else{//输出第一次出现的子串下标及剩余字符 
		printf("%d %s\n",p,string+p);
	}
	return 0;
}
#include<bits/stdc++.h>
#define N 1001
using namespace std;
void GetNext(char t[],int next[])
{
	int j,k;
	j=0;
	k=-1;
	next[0]=-1;//第一个无前缀匹配,为-1 
	while(j<(strlen(t)-1)){
		if(k==-1||t[j]==t[k]){//k==-1或者匹配成功继续向下匹配 
			j++; 
			k++;
			next[j]=k;
		}
		else{
			k=next[k];
		}
	}	
} 
int KMPIndex(char s[],char t[])
{
	int next[N]={0};
	GetNext(t,next);
	int s1=strlen(s);
	int t1=strlen(t);
	int i=0;
	int j=0;
	while(i<s1&&j<t1){
		if(j==-1||s[i]==t[j]){//j=-1,j++使得j又重新指向模式串第一个元素 
			i++;//匹配,继续匹配下面的元素 
			j++;
		}
		else{
			j=next[j];//i不变,j后退 
		}
	}
	if(j>=t1){
		return (i-t1);
	}
	else{
		return -1;
	}
}
int main()
{
	char a[N];
	cout<<"请输入主串:";
	gets(a);
	char b[N];
	cout<<"请输入模式串:";
	gets(b);
	int t=KMPIndex(a,b);
		cout<<t<<endl; 
	return 0; 
}

package KMP;

import java.util.Arrays;
import java.util.Scanner;

public class KMP {
    public static int[] prefix(String pattern){//获得模式串前后缀匹配字符串的长度
        //并存入数组中
        int len = pattern.length();//获取模式串的长度
        int[] result = new int[len];//创建和模式串等长的存取匹配长度的数组
        result[0] = 0;//result的第一个元素无匹配的值,所以其匹配长度肯定为0
        int p = 0;//标志模式串匹配的长度
        int i = 1;//从1开始遍历剩下的字符并判断
        while (i<len){
            if (pattern.charAt(i) == pattern.charAt(p)){//相等的情况
                p++;//匹配长度+1
                result[i] = p;//赋给数组对应位置
                i++;//继续向后判断
            }else{//不相等的情况
                if (p>0) {//没到第一个位置,预防越界
                    p = result[p - 1];//p等于匹配长度数组的前一个位置,即侧着移位
                }else{//不相等且到了第一个位置
                    result[i] = p;//赋0
                    i++;//继续向后判断
                }
            }
        }
        return result;
    }

    public static void change(int[] nums){
        for (int i = nums.length-1; i >0; i--) {//将所有匹配长度前移,形成统一
            nums[i] = nums[i-1];
        }
        //给nums[0]赋值
        nums[0] = -1;
    }

    public static void kmpSearch(String nums,String pattern){
        int[] array1 = prefix(pattern);//array1为统计前后缀匹配长度的数组
        change(array1);
        int i = 0;
        int j = 0;
        while(i<nums.length()){
            if (j==pattern.length()-1&&nums.charAt(i)==pattern.charAt(j)){//说明匹配完了,成功
                System.out.println("在主串中下标为:"+(i-pattern.length()+1)+"的位置定位到了模式串");
                //匹配完成后,继续向后匹配
                j = array1[j];
            }
            if (nums.charAt(i)==pattern.charAt(j)){//相等同时挪动
                i++;
                j++;
            }else{//不相等
                j = array1[j];//将j移到其对应的已匹配的地方,从此位置继续匹配
                if (j == -1){//说明没有匹配的,将主串和模式串的指针都移动
                    i++;
                    j++;
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("请输入主串:");
        String nums = in.next();
        System.out.print("请输入模式串:");
        String pattern = in.next();
        kmpSearch(nums,pattern);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值