字符串--KMP算法理解(python)

完全版

import random
def vo(mainstr,substr):#暴力对比时间
    ind=0
    n=0
    timer=0
    result=[]
    while (ind <= len(mainstr) - len(substr)):
        timer=timer+1
        if mainstr[ind+n]==substr[n]:
            n=n+1
            if n==len(substr):
                result.append(ind)
                ind = ind + 1
                n = 0
        else:
            ind=ind+1
            n=0
    print('暴力对比结果为:'+str(result))
    print('暴力对比次数为:'+str(timer))


def table(substr):#自匹配表#2
    t=[]
    if len(substr)==1:
        tt = [[0 for j in range(len(substr))] for i in range(2)]
        return tt,1
    else:
        for i in range(1,len(substr)):  #被对比的子串
            ind=0
            for j in range(len(substr)):    #逐个对比后,记录匹配成功数
                if i+j>len(substr)-1:           #当主子串被匹配完,添加到表
                    t.append(ind)
                    break
                else:
                    if substr[j]==substr[i+j]:  #内循环对比成功一个,加1
                        ind=ind+1
                    else:                       #直到匹配失败,添加到表
                        t.append(ind)
                        break
        ind=0
        tt = [[0 for j in range(len(t))] for i in range(2)]
        for i in range(len(t)):
            if t[i]!=0:
                tt[0][ind]=i
                tt[1][ind]=t[i]
                ind=ind+1
        return tt,ind  #自匹配表,t[0]为索引位置,t[0][0]为匹配数量

def next(ind,n,t,node_nums,m):#3 node_nums是t中自匹配点数量
    max_ind=0
    max_val=0
    if n == 0:
        ind = ind + 1 + m  # 第一个就匹配失败,下一位
        m = 0
    else:
        if n - 1 >= t[0][0] + 1:  # 若超越第1个相似点
            for i in range(node_nums):  # 寻找超越点内的最大连续位置
                if n - 1 >= t[0][i] + 1:
                    if t[1][i] > max_val:
                        max_val = t[1][i]
                        max_ind = i
                else:
                    break
            m = n - t[0][max_ind] - 1
            if m == t[1][max_ind]:  # 后串与表一致,所以需要请出前串
                ind = ind + n - m
            else:
                ind = ind + n  ###############
                m = 0
        else:
            # ind = ind + n + 1 # 否则直接跳尾
            # m = 0
            ind = ind + n  # 否则直接跳尾
            m = 0
    return ind,m,max_ind

def compare(mainstr,substr):#1
    ind=0   #头指针,指定当前子串的头到了主串的哪
    n=0     #尾指针,记录对比结果
    m=0     #内指针,无需在头指针的新位置开始对比,根据前一个对比结果,指明子串内部某位置开始
    t,node_nums=table(substr)
    save=[]
    timer = 0
    while(ind<=len(mainstr)-len(substr)):#最多查找到末尾回来一点
        print('当前索引ind=' + str(ind))
        print('当前剩下主串为:'+mainstr[ind:])
        max_ind=0
        for n in range(len(substr)-m):    #根据m来确定从多少开始对比
            timer=timer+1
            #print('已对比次数:'+str(timer)+';当前对比字符为主串的'+mainstr[ind+n+m]+'和子串的'+substr[n+m])
            if mainstr[ind+n+m]==substr[n+m]:
                n=n+1
            else:
                ind,m,max_ind=next(ind, n, t, node_nums,m)  #计算下一个匹配位置
                break
        if n==len(substr)-m:
            save.append(ind)
            m = n - t[0][max_ind] - 1
            if m == t[1][max_ind]:# 只有当m==表信息,需要前串亲自上场时,才需要用到m作为偏移
                ind = ind + n - m
            else:
                ind = ind + n+1# 这里的+1曾经出现过bug,去掉1会慢一点,但不会有bug
                m = 0
    print('kmp对比次数为:'+str(timer))
    print('主串+子串长度为:'+str(len(mainstr)+len(substr)))
    return save


def rand_str(length):
    str_0 = []
    for i in range(length):
        str_0.append(random.choice("abcde"))
    return str_0

mainstr=''.join(rand_str(300))
substr=''.join(rand_str(3))

print('待查找的主串为:'+mainstr)
print('子串为:'+substr)

vo(mainstr,substr)
result=compare(mainstr,substr)
if result!=[]:
    print('主串中匹配到的索引号为'+str(result))
else:
    if substr in mainstr:
        print('本程序出错,该匹配到的没匹配到')
    else:
        print('没有查找到结果')

以下为简易版,方便在线笔试快速编写

def table(substr):
    t=float('inf')
    if len(substr)==1:
        return t,1
    else:
        for i in range(1,len(substr)): #被对比的子串,简易版只需一个头位置就可以加速很多
            if substr[0]==substr[i]:
                t=i
                break
        return t

def compare(mainstr,substr):
    ind=0
    n=0
    t=table(substr)
    save=[]
    timer = 0
    while(ind<=len(mainstr)-len(substr)):#最多查找到末尾回来一点
        print('当前对比主串为:'+mainstr[ind:])
        print('当前索引:'+str(ind))
        max_ind = 0
        max_val=0
        for n in range(len(substr)):
            timer=timer+1
            if mainstr[ind+n]==substr[n]:
                n=n+1
            else:
                if n==0:
                    ind = ind + 1  #第一个就匹配失败,下一位
                    break
                else:
                    if n-1>=t+1: #若超越第1个相似点
                        ind = ind + t
                    else:
                        ind=ind+n+1
                    break
        if n==len(substr):
            save.append(ind)
            if n - 1 >= t + 1:  # 若超越第1个相似点
                ind = ind + t
            else:
                ind = ind + n+1
    print('总对比次数为:'+str(timer))
    print('主串+子串长度为:'+str(len(mainstr)+len(substr)))
    return save

mainstr='ABCABCABDABkkkkkk'
substr='ABCAB'
简易版显示结果:
当前对比主串为:ABCABCABDABkkkkkk
当前索引:0
当前对比主串为:ABCABDABkkkkkk
当前索引:3
当前对比主串为:ABDABkkkkkk
当前索引:6
当前对比主串为:ABkkkkkk
当前索引:9
当前对比主串为:kkkkk
当前索引:12
总对比次数为:17
主串+子串长度为:22
主串中匹配到的索引号为[0, 3]
    原文作者:KMP算法
    原文地址: https://blog.csdn.net/yuwenyuanxi/article/details/82179394
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞