字符串匹配

字符串匹配问题:

假设文本是一个长度为n的数组 T[1...n],而模式是一个长度为m的数组 P[1...m],其中m<=n,进一步假设P和T的元素都是来自一个有限字母集的字符串,例如{0,1}或{a,b,...,z}.字符数组 P T 通常称为字符串。

如果0<=s<=n-m,并且 T [s+1...s+m]=P [1...m]那么称模式P在文本T中出现,且偏移为3;

字符串匹配问题就是找到所有的有效偏移,使得在该有效偏移下,所给的模式 P 出现在给定的文本 T 中。

朴素字符串匹配算法

通过一个循环找到所有有效偏移,该循环对n-m+1个可能的s值进行检测,看是否满足条件T [s+1...s+m]=P [1...m]

T="abaabba"
P="baa"
w=[]
for i in range(len(T)-len(P)+1):
    flage=True
    for j in range(len(P)):
        if P[j]!=T[i+j]:
            flage=False
    if flage:
        w.append(i)
print(w)

Rabin-Karp算法:

步骤:

1.将字符串进行数字编码,

2.查找 T [s+1...s+m]的散列地址=P [1...m]的散列地址 的偏移量s

如果T [s+1...s+m]=P [1...m],则T [s+1...s+m]的散列地址=P [1...m]的散列地址(必然条件,非充分条件,即T [s+1...s+m]的散列地址=P [1...m]的散列地址 ,无法推出T [s+1...s+m]=P [1...m],因为存在散列冲突),故散列地址相同后需要进行字符串对比;

3.为进一步压缩匹配时间,运用霍纳法则,根据Ts计算Ts+1,去掉左侧数字,增加右侧数字

T="abaabba"
P="baa"
t,p='',''
for ts in T:
    if ts=='a':
        t+='0'
    else:
        t+='1'
for ps in P:
    if ps=='a':
        p+='0'
    else:
        p+='1'
w=[]
for i in range(len(T)-len(P)+1):
    if int(t[i:i+len(P)])%13==int(p)%13:
        flage = True
        for j in range(len(P)):
            if P[j] != T[i + j]:
                flage = False
        if flage:
            w.append(i)
print(w)

利用有限自动机进行字符串匹配:

Knuth-Morris-Pratt算法

比较指针的回溯导致暴力匹配效率较低,KMP算法可以做到仅仅移动模式串,比较指针不回溯

前缀:一个字符串从前往后的所有子串(不包含尾部字符)

后缀:一个字符串从后往前的所有子串(不包含首部字符)

最长公共前后缀:前缀和后缀的最长的共有元素的长度

以“ABCDABD”为例,求解每个位置的最长公共前后缀长度:

模式字符串移动位数= 模式字符串与文本字符串以匹配的位数-已匹配字符串(模式字符串的子字符串)的最长公共前后缀

def compute_prefix_function(string):
    n=len(string)
    next=[0]*n
    k=0        #k指向前缀待匹配的位置
    for i in range(1,n):
       #前一子串有最长公共前后缀,且待匹配位置与当前位置元素不相同,查找再往前的一个最长公共前后缀
        while k>0 and string[k]!=string[i]: 
            k=next[k-1]
        if string[k]==string[i]:           #待匹配位置与当前位置元素相同
            k+=1
        next[i]=k
    return next
print(compute_prefix_function("ABCDABD"))

方式1,通过计算位置移动匹配

def compute_prefix_function(string):
    n=len(string)
    next=[0]*n
    k=0        #k指向前缀待匹配的位置
    for i in range(1,n):
        while k>0 and string[k]!=string[i]: #前一子串有最长公共前后缀,且待匹配位置与当前位置元素不相同,查找再往前的一个最长公共前后缀
            k=next[k-1]
        if string[k]==string[i]:           #待匹配位置与当前位置元素相同
            k+=1
        next[i]=k
    return next
def KMP_MATCHER(str_1,str_2):
    n=len(str_1)
    m=len(str_2)
    next=compute_prefix_function(str_2)
    k=0
    flage=False
    while k<n:
        if str_1[k]!=str_2[0]:
            k+=1
        else:
            k0=0
            for i in range(m):
                if k+i<n and str_1[k+i]==str_2[i]:
                    k0+=1
                else:
                    break
            if k0==m:
                flage=True
                return k
            else:
                k+=k0-next[k0-1]
    if not flage:
        return -1
print(KMP_MATCHER("BBC ABCDAB ABCDABCDABDE","ABCDABD"))
print(KMP_MATCHER("BCABCDAB","ABCDABD"))
# print(compute_prefix_function("ABCDABD"))

方式2:

def KMP_MATCHER_1(str_1,str_2):
    n = len(str_1)
    m = len(str_2)
    next = compute_prefix_function(str_2)
    k = 0
    for i in range(n):
        while k > 0 and str_2[k] != str_1[i]:  # 前一子串有最长公共前后缀,且待匹配位置与当前位置元素不相同,查找再往前的一个最长公共前后缀
            k = next[k-1]
        if str_2[k] == str_1[i]:  # 待匹配位置与当前位置元素相同
            k += 1
        print(k)
        if k==m:
            k=next[k-1]
            return i-m+1

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值