03.串匹配算法

 
def naive_matching(t, p):
    """
    朴素的串匹配算法
    从左到右逐个字符匹配,发现不匹配时,转去考虑目标串里的下一个位置是否与模式串匹配
    :param t: 目标串
    :param p: 模式串
    :return:
    """
    m, n = len(p), len(t)
    i, j = 0, 0
    while i < m and j < n:
        if p[i] == t[j]:
            i, j = i + 1, j + 1
        else:
            i, j = 0, j - i + 1
    if i == m:
        return j - i
    return -1


def matching_kmp(t, p, pnext):
    """KMP串匹配
    如果i = -1,或者当前字符匹配成功(即t[j] == P[i]),都令i++,j++,继续匹配下一个字符;
    如果i != -1,且当前字符匹配失败(即t[j] != P[i]),则令 j 不变,i = next[i]
    next[j]即为j所对应的next值,此举意味着失配时,模式串P相对于目标串t向右移动了i - next [i] 位。
    :param t: 目标串
    :param p: 模式串
    :return:
    """
    i, j = 0, 0
    m, n = len(p), len(t)
    while i < m and j < n:  # i==m说明找到匹配
        if i == -1 or p[i] == t[j]:  # 遇到-1或字符相等,匹配下一对字符
            i, j = i + 1, j + 1
        else:  # 从pnext取的p的下一字符位置
            i = pnext[i]
    if i == m:  # 找到匹配,返回其开始下标
        return j - i
    return -1  # 无匹配,返回特殊值


def gen_pnext(p):
    """
    生成针对p中各位置i的下一检查位置表
    计算最长相等前后缀的长度--递归求法
    已知next [0, ..., i],如何求出next [i + 1]呢?
    对于P的前i+1个序列字符:
    若p[i] == p[k],则next[i + 1 ] = next [i] + 1 = k + 1;
    若p[k ] ≠ p[i],如果此时p[ next[k] ] == p[i],则next[ i + 1 ] =  next[k] + 1,
    否则继续递归前缀索引k = next[k],而后重复此过程
    """
    i, k, m = 0, -1, len(p)
    pnext = [-1] * m  # 初始数组元素全为-1
    while i < m - 1:  # 生成下一个pnext元素值
        if k == -1 or p[i] == p[k]:
            i, k = i + 1, k + 1
            pnext[i] = k  # 设置pnext元素
        else:
            k = pnext[k]  # 退到更短相同前缀
    return pnext


if __name__ == '__main__':
    print(naive_matching("ababc", "abc"))  # 2
    print(gen_pnext("abbcabcabbcaa"))  # [-1, 0, 0, 0, 0, 1, 2, 0, 1, 2, 3, 4, 5]
    print(gen_pnext("ababaaababaa"))  # [-1, 0, 0, 1, 2, 3, 1, 1, 2, 3, 4, 5]
    print(matching_kmp("ababc", "abc", gen_pnext("abc")))  # 2
"""参考博客:https://blog.csdn.net/dl962454/article/details/79910744"""

 优化后的next求法:

def gen_pnext2(p):
    """
    生成针对p中各位置i的下一检查位置表
    优化:
     p[i] != t[j]时,下次匹配必然时p[next[i]]与t[j]匹配,如果p[i]=p[next[i]]
     必然p[next[i]]!=t[j],匹配失败,需要再次递归,即令next[i] = next[ next[i] ]。
    """
    i, k, m = 0, -1, len(p)
    pnext = [-1] * m  # 初始数组元素全为-1
    while i < m - 1:  # 生成下一个pnext元素值
        # p[k]表示前缀,p[i]表示后缀 
        if k == -1 or p[i] == p[k]:
            i, k = i + 1, k + 1
            if p[i] == p[k]:
                # 因为不能出现p[i] = p[ next[i]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
                pnext[i] = pnext[k]
            else:
                pnext[i] = k
        else:
            k = pnext[k]  # 退到更短相同前缀
    return pnext

 

posted @ 2019-10-19 16:01  fly_bk  阅读(214)  评论(0编辑  收藏  举报