zoukankan      html  css  js  c++  java
  • KMP笔记

    KMP算法,字符串的模式匹配。

    简单的匹配也是可以的,而且很容易就想到,只是在时间上浪费了很多不必要的比较。

    那么在真正匹配之前对目标子串做一个处理,将会在一定程度上省去很多不必要的比较,大大提高效率。

    比如,target:ababaaaba  这样一个字符串在str字符串中去寻找匹配。那么首先对当前target字符串做一个处理,所谓处理就是记录一下那些字符是与前面重复了,再匹配过程中已经想等就不用再重复比较,通常为target处理得到一个next数组,这个数字就是记录到当前有多少个字符与重复相等的。具体:

    next【i】:

    0,  i=0或者没有前缀后缀字符相等;

    max{k|0<=k<=j,p0...pk=pj-k...pj}集合非空,即有前后缀相等;

    例子:ababaaaba

    next: next【0】=0;

        next【1】=0(a!=b);

        next【2】=1(aba中a==a);

        next【3】=2(abab中ab=ab);

        next【4】=3(ababa中aba=aba);

        next【5】=1(ababaa中abab != abaa, j回溯往前为next[2]=1, a[1]!=a[5], 则j继续回溯为next[0]=0,a[0]==a[5]);

        next【6】=1(j=1,但a[1]!=a[6],所以j回溯为a[0]=0,a[0]==a[6]);

        next【7】=2(a[1]==a[7]);

        next【8】=3(a[2]==a[8]);

    获取next数组的函数:

    vector<int> getNext(const string &needle)
    {
        vector<int> next(needle.size());
        if (needle.size() == 0)
            return next;
        next[0] = 0;
        int i = 1, j = 0;
        while (i<needle.size())
        {
            if ( needle[i] == needle[j])  //前面最后一个字符等于后面最后一个字符,所以next数组当前标记为j+1
                next[i++] = ++j;
            else if (j != 0)
                j = next[j - 1];//不等的时候,j往前回溯一个,跳到上一个比较下来相等的位置下标
            else
                next[i++] = 0;  //前后的后缀字符不等,且此时j为0,即与第一个字符比较既然不等,next数据当然将其标记为0
        }
        return next;
    }

    得到next数组之后,在匹配中用两个指针,i表示str中下标位置,j表示target中下标位置。每次逐一比较,如果相等依次往后推进,如果不等,则让j回溯到上一next【j】,即中间重复的就不再比较辣,下次开始从回溯的位置开始比较。

    int find(const string &haystack, const string &needle, int pos)
    {
        if (haystack.size()<needle.size() || pos<0 || pos>haystack.size() - 1)
            return -1;
        vector<int> next = getNext(needle);
        int i = pos, j = 0;
        while (i<haystack.size() && j<needle.size())
        {
    
            if (haystack[i] == needle[j])   //如果两个字符串的当前字符相等,则一次往后推一个就好
            {
                ++i;
                ++j;
            }
            if (j == needle.size())    //如果,目标字符已经达到最末尾,则可以返回超找到的位置了。
                return i - j;
            if (i<haystack.size() && haystack[i] != needle[j]) //如果当前比较的字符不相等,则按照next数组中往上回溯一个下标,再次比较即可
            {
                if (j != 0)//简单的j按照next数组回溯
                    j = next[j - 1];
                else    //目标字符串的第一个字符就没有匹配上,则让长的字符数组往后推进一个再进行比较
                    ++i;
            }
        }
        return -1;
    }

    以上代码为从str中的pos位置开始往后查找匹配。

    next 数组改进:每次对于是与第一个字符比较还是中间的字符比较区分开来,即记录上中间已经相等的字符,匹配的时候可以直接略过。

    如:aaaabcdef中匹配aaaaax

    现在当匹配到第四个d与a不等时,不用再去比较b和a前面的a,直接跳到第一个a,因为中间的a是相等的,在nextval数组中已经记录略过了

    vector<int> GetNextVal(const string &str)
    {
    	vector<int> nextVal(str.size());
    	if (str.size() == 0)
    		return nextVal;
    	int i = 0,j = 0;
    	while (i<str.size()-1)
    	{
    		if (j == 0)
    			nextVal[++i] = j++;
    		else if (str[i] == str[j - 1])
    			nextVal[++i] = nextVal[j++];
    		else
    			j = nextVal[j - 1];
    	}
    	return nextVal;
    
    }
    
  • 相关阅读:
    移动性能测试 | 持续集成中的 Android 稳定性测试
    iOS 测试 | iOS 自动化性能采集
    Google 测试总监聊如何经营成功的测试职业生涯
    浅谈一下可扩展性网站架构设计
    一条SQL执行慢的原因有哪些
    为什么在做微服务设计的时候需要DDD?
    是时候拥抱.NET CORE了
    MySql多表查询优化
    九种高性能可用高并发的技术架构
    HTTP协议总结
  • 原文地址:https://www.cnblogs.com/weiyi-mgh/p/6758790.html
Copyright © 2011-2022 走看看