字符串专题(匹配,子序列)

序列模式匹配

给定文本text和待匹配字符串pattern,二者皆只包含小写字母,并且不为空。
在text中找出匹配pattern的最短字符串,匹配指按序包含pattern,但不要求pattern连续。
如text为abaacxbcbbbbacc,pattern为cbc,text中满足条件的是abaacxbcbbbbacc下划线部分。

输入为每行两个字符串,前者为模式串s1,后者为待匹配串s2。

输出最短匹配序列起止位置(位置下标从0开始),用空格分隔。若有多个答案,输出起止位置最小的答案;若无满足条件的答案,则起止均为-1。

思路:共进退法

例子是s1=abcabcbc 和 s2=cbc。  i为s1的索引,j为s2的索引。下面演示i,j如何共进退

现在s1中找出包含s2的第一个区间:cabc,此时索引i=5,j=2。那么这时j已经指向了子串末尾,i也指向了字串末尾(i,j共进)。那怎样获取子区间首尾位置呢?其实现在i就是尾部位置,用k=i记录下来。然后获取头部位置:就是将i往回倒退,同时j也往回倒退,直到j倒退到子串第一个元素,此时i也退到了字串的第一个元素(i,j共退)。此时k-i+1的长度就是包含子串的子区间长度。此时记录首尾位置和子区间长度。现在i位于这个子区间的首元素下一位,j位于s2的头部,又开始了新一轮的查找(共进退)。循环直到i超出s1的长度结束。

有了上面的共进退思想,代码就很好理解了:

def match(s1, s2):
    if len(s1)<len(s2):return(-1,-1)
    i,j=0,0
    l,r,min_l = -1,-1,len(s1)+1   # l,r分别为子区间的首尾位置
    while i<len(s1): 
        if s1[i]==s2[j]:          # 如果当前i,j两个指针所指元素一样,
            if j==len(s2)-1:      # 则判断j是否已经走到了串尾
                k = i             # 如果是,则此时找到了一个子区间,用k标记尾部
                while(j>=0):      # i和j共退,来得到头部位置
                    if s1[i]==s2[j]:
                        j-=1
                    i-=1
                i+=1              # 得到了头部位置i 
                temp_min = k-i+1  # 子区间长度就是k-i+1 
                if temp_min<min_l:       # 如果这个子区间长度更小
                    l,r,min_l = i,k,temp_min    # 则更新左右边界和子区间长度
            else:                 # 如果此时两串所指元素一样,但还没有到末尾,则i,j继续向前
                i+=1
                j+=1
        else:     # 如果i,j指向元素不一样,则i继续走呀
            i+=1
    return (l,r)
while True:
    try:
        s1,s2 = input().split(' ')
        l,r = match(s1,s2)
        print(l,r)
    except:
        break

 

posted @ 2019-07-11 21:18  三年一梦  阅读(1267)  评论(0)    收藏  举报