字符串模式匹配(基于Python和C算法)

简单的模式匹配算法

这种算法的关键在于,指针i会进行回退,而模式串不去移动。这里的算法关键之处在于理解指针ij的处理关系。算法时间复杂度为:

简单的模式匹配算法时间复杂度
,并且 m为主串, n为模式串。


# use type hint to get a smart hint
def index(target: str, pattern: str) -> int:
    # 这一波使用普通的算法,即i回退
    i = 0
    j = 0
    len_i = len(target)
    len_j = len(pattern)
    while (i<= len_i-1) and (j<=len_j-1):
        if target[i] == pattern[j]:
            i += 1
            j += 1
        else:
            # 回退
            i = i - j + 1
            print ("i value: %d"%i)
            j = 0
        print("j is %d"%j)
    if (j > len_j-1):
        return i - len_j
    else:
        return -1

print(index("abc", "abc"))
print(index("aabc", "abc"))
字符串模式匹配(基于Python和C算法)_第1张图片
算法关键点解释

而上述也是写简单的模式匹配算法中应该注意的关键,这是算法出不出错的最关键的保证。

改进的模式匹配算法——KMP算法

KMP算法全称为克努特—莫里斯—普拉特操作,此算法时间复杂度为:

KMP算法时间复杂度
,其与之前算法最大的不同在于:指针 i不回退,而是移动模式串。因此,算法的关键部分在于,在匹配不成功的时候,我们该如何移动模式串呢,又该移动多少位的模式串呢?
因此,这里就涉及到 next[]数组的计算了,而计算 next[]数组的时候我们必须要明白为何要这样子去计算。我们称计算 next[]的函数为失效函数。
字符串模式匹配(基于Python和C算法)_第2张图片
失效函数

Python代码如下:

def get_next(pattern:str) ->list:
    # 求失效函数值序列
    length = len(pattern)
    next = [None]*length
    next[0] = -1  # 失效序列初始值设置为-1(某些国内教材设定为0)
    i = 0
    j = -1  # represents the value of next[]
    while (i < length-1):
        # mmp C/C++写多了,括号习惯了
        if (j == -1 or pattern[i] == pattern[j]):
            # j == -1 means this is the first value, need process specially
            i += 1
            j += 1
            next[i] = j
        else:
            j = next[j]
    return next

上述代码里,最关键也是最难理解的部分就是while循环里面的内容。而while循环实际上是通过前面计算的数据去求得后面next数组的值。

字符串模式匹配(基于Python和C算法)_第3张图片
前缀方式理解代码

另外,还有一种改进的方式:

def get_nextval(pattern:str) ->list:
    # 改进的失效函数序列值算法
    length = len(pattern)
    next = [None]*length
    next[0] = -1  # 失效序列初始值设置为-1(某些国内教材设定为0)
    i = 0
    j = -1  # represents the value of next[]
    while (i < length-1):
        if (j == -1 or pattern[i] == pattern[j]):
            # j == -1 means this is the first value, need process specially
            i += 1
            j += 1
            if pattern[i] == pattern[j]:
                next[i] = next[j]
            else:
                next[i] = j
        else:
            j = next[j]
    return next

这里的关键部分在于:

if pattern[i] == pattern[j]:
                next[i] = next[j]
            else:
                next[i] = j

这里的意思是,既然我target[i]pattern[i]进行了对比,然后发现本身就已经不同了,那我就该回退至pattern[next[i]]的值,可是这个值如果和pattern[i]相同的话,那就没有对比的必要了,也就省去了一次多余的比较了。

KMP算法

def kmp(target:str, pattern:str) -> int:
    # kmp(非改进)算法,模式匹配
    next = get_next(pattern)  # 得到失效值序列
    i = 0
    j = 0
    len_i = len(target)
    len_j = len(pattern)
    while i<=len_i-1 and j <=len_j-1 :
        if target[i] == pattern[j] or j == -1:
            i += 1
            j += 1
        else:
            j = next[j]
            # if j == -1:
            #     i += 1
            #     j += 1
    
    if j >= len_j-1:
        return i-len_j
    else:
        return -1

这里可以注意下我们注释的部分,因为Python里面list[-1]表示取数组最后一项,而我们这里-1代表需要使i++,所以我们要处理好这部分的逻辑。

C语言实现过程

/*
written at 2018-5-28 19:27:42
KMP算法中计算next的实现方式
*/
#include 
#include 

void next1(char *s, int *next){
    // s为字符串,next为失效函数序列
    int length = strlen(s);  // 字符串的长度
    int i = 0; int j = -1;
    next[0] = -1;
    while (i < length-1){
        if (j==-1 || s[i] == s[j]){i++; j++;next[i] = j;}
        else{j = next[j];}
    }
}

void next_val(char *s, int *next){
    // 改进版本的失效函数
    int length = strlen(s);
    int i = 0; int j = -1;
    next[0] = -1;
    while (i= length_pat) return i - length_pat;
    else return 0;  // pat fail
}

void main(){
    char *s = "peng jidong";
    char *pat = "jidong";
    int ans = kmp(s, pat, 1); 
    printf("ans is %d\n", ans);
    // printf the answer to examine it right or not
    for (int i=ans; i

你可能感兴趣的