从来就没有救世主  也不靠神仙皇帝  要创造人类的幸福  全靠我们自己  

字符串操作相关算法

 

    子串在主串中的定位操作:串的模式匹配

1. 一般性的匹配方法

(1)相当于 string类的find方法-----找子串在主串中第一次出现的位置

 1 int findSub(string str,string sub)
 2 {
 3     int strLen = str.size();
 4     int subLen = sub.size();
 5     int i = 0,j = 0;
 6     while(i<strLen && j<subLen) {
 7         if(str[i] == sub[j]) {
 8             ++i;
 9             ++j;
10         }
11         else {
12             i = i - j + 1;  //这个很关键,i只前进一步
13             j = 0;
14         }
15     }
16     if (j == subLen) {
17         //匹配成功
18         return (i-j);
19     }
20     else {
21         return -1;
22     }
23 }

 

 

(2)换成for循环

int findSub_v2(string str,string sub)
{
    int strLen = str.size();
    int subLen = sub.size();
    for(int i=0;i<(strLen-subLen+1);i++) {
        int j = 0;
        for(j=0;j<subLen;j++) {
            if(str[i+j] == sub[j]) {
                continue;
            }
            else {
                break;
            }
        }
        if(j == subLen) {
            return i;
        }
    }
    return -1;
}

  代码同(1)的没有什么区别

  设主串长n,子串长m

    最好情况:子串在最前面,只要第一次循环一次,第二个循环m次

    最差情况:子串在最后面,一共要循环 (n-m+1)*m 次  O(mn)的时间复杂度

 

 

2. KMP 模式匹配算法  O(m+n)

  note:(1)、(2)中的图从《大话》中截来,它里面的字符下标从1开始

(1)下面是一般的匹配算法:

 

   while形式的循环里面,每次遇到不等的字符的时候,i要回溯(回溯的位置i-j+1使得i每次循环完子串后只增长1个单位,也就相当于版本2的for循环)。

  原来的实现基本意思是:每次比较到不等地方,子串下标从0开始,而主串只加1

 

  问题:

    上面第②步,因为子串中第一个和第二个字符不等,而子串的第二个字符与主串的第二个字符相等,所以没必要进行第②步

  上面的那么多步,实际上只需要下面两步:

           

 

 

(2)子串中有重复字符的情况

  

 

 

  实际上,只需要第①、⑥步:

 

 

 

(3)KMP

  在while循环里,使i不回溯(每比较一次,i就自增一次)。根据子串本身的特点,每次调整 j 的位置

  j 的值:next数组

      如果没有重复字符,则子串下标每次要回到 0,而主串下标依然在之前比较却不相等的那个地方。

    如果有重复字符:YY一下(2)中的内容,如果我们比较到第3个字符'c'不等的话,那还是要从子串的下标0开始;

                                            如果比较到4个字符'a'不等,那还是要回到0

                                            如果比较到第5个字符'b'不等,则只要回到1就行了(这里实际上还可以优化,即KMP的优化,在(4))

             如果比较到第6个字符'x'不等,则j回到第3个字符进行比较就可以了

 

  代码如下:参见 https://segmentfault.com/a/1190000008575379

  未优化的:

static void getNext(string p,int next[])
{
    int len = p.size();
    int i = 0;
    int j = -1;
    next[0] = -1;
    while(i<len) {
        if(j==-1 || p[i] == p[j]) {
            i++;
            j++;
            next[i] = j;
        }
        else {
            j = next[j];
        }
    }
}

  优化后的求next数组:

static void getNext_v2(string p,int next[])
{
    int len = p.size();
    int i = 0;
    int j = -1;
    next[0] = -1;
    while(i<len) {
        if(j==-1 || p[i] == p[j]) {
            i++;
            j++;
            if(p[i] != p[j]) {
                next[i] = j;
            }
            else {
                next[i] = next[j];
            }
        }
        else {
            j = next[j];
        }
    }
}

 

  kmp搜索算法本身:用while循环

 1 int kmpSearch(string str,string sub)
 2 {
 3     int strlen = str.size();
 4     int sublen = sub.size();
 5     int * next = (int *)malloc((sublen+1)*sizeof(int));
 6     if(next == nullptr) {
 7         return -1;
 8     }
 9     int i = 0;
10     int j = 0;
11 //    getNext(sub,next);
12     getNext_v2(sub,next);
13     while(i<strlen && j<sublen) {
14         if(j==-1 || str[i] == sub[j]) {
15             i++;
16             j++;
17         }
18         else {
19             j = next[j];  //只是这里与(1)里面的不同,i不再回溯了
20         }
21     }
22     if(j == sublen) {
23         //匹配上了
24         return i-j;
25     }
26     if(next != nullptr) {
27         free(next);
28     }
29     return -1;
30 }

 

posted @ 2020-02-21 22:20  T,X  阅读(285)  评论(0编辑  收藏  举报