3823 寻找字符串(kmp)

1. 问题描述:

给定一个由小写字母构成的字符串 s。请你找到一个满足如下所有要求的字符串 t:
字符串 t 是字符串 s 的前缀。
字符串 t 是字符串 s 的后缀。
字符串 t 在字符串 s 的中间出现过。也就是作为一个既非前缀也非后缀的子串出现过。
字符串 t 的长度应尽可能长。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。每组数据占一行,包含一个字符串 s。

输出格式

每组数据输出一行结果,如果 t 存在,则输出 t,否则输出 not exist。

数据范围

前三个测试点满足 1 ≤ |s| ≤ 20。
所有测试点满足 1 ≤ T ≤ 10,1 ≤ |s| ≤ 10 ^ 6。
同一测试点内所有输入字符串 s 的长度之和不超过 10 ^ 6。

输入样例:

2
fixprefixsuffix
abcdabc

输出样例:

fix
not exist
来源:https://www.acwing.com/problem/content/3826/

2. 思路分析:

这道题目考察的是字符串匹配,字符串匹配可以使用字符串哈希或者kmp算法,这里使用kmp算法对字符串进行匹配,能够将时间复杂度控制在O(n)之内。主要考察对kmp算法的理解是否到位,kmp算法的核心是next数组含义的理解以及next数组的求解,kmp算法中字符串的下标一般从1开始,这样可以方便后面求解next数组,其中next[i]表示前i个字符最长的和前缀相等的后缀的长度(最长的前后缀是可以重叠的但是最长的长度为n - 1,也即匹配到末尾的上一个位置),我们使用kmp算法求解next数组之后next[n]就是前n个字符最长的和前缀相等的后缀的长度,如何找到所有前n个字符和前缀相等的后缀呢?这里需要使用到一个结论,① l1 = next[n];② l2 = next[l1]... lk,一直到lk等于0就结束了,l1~lk都是前n个字符所有与前缀相等的后缀的长度;我们求解出next数组之后将长度为1~n - 1的next数组的值放到一个数组st或者哈希表中,然后从l1开始枚举,如果枚举的过程中发现数组对应next[i]的值为1的时候说明小于长度n的字符串中存在过当前对应的前缀长度,也即当前的前缀也在中间出现过所以是满足要求的,因为是从长度较大的开始枚举所以第一次枚举到的肯定就是答案。其实也比较容易理解,因为当前最长的前缀长度为l1 = next[n],相当于第l1个位置不匹配需要跳到next[l1]的位置继续匹配在st中找到了对应长度的前缀或者是lk = 0为止。

3. 代码如下:

class Solution:
    def process(self):
        T = int(input())
        for c in range(T):
            s = input()
            n = len(s)
            # 在s的前面添加一个字符这样s元素的下标可以从1开始, 方便后面处理
            s = "0" + s
            ne = [0] * (n + 10)
            j = 0
            # 求解ne的值
            for i in range(2, n + 1):
                while j and s[i] != s[j + 1]: j = ne[j]
                if s[i] == s[j + 1]: j += 1
                ne[i] = j
            # st用来标记对应长度的ne列表的值
            st = [0] * (n + 10)
            # 注意是循环到长度n - 1
            for i in range(1, n):
                st[ne[i]] = 1
            # 求解是否存在长度小于n的字符串使得前缀的长度等于ne[i]
            i = ne[n]
            res = -1
            # 从长度较大的开始枚举, 第一个找到的肯定是最长的
            while i > 0:
                if st[i]:
                    res = i
                    break
                i = ne[i]
            if res == -1: print("not exist")
            else:
                print(s[1: i + 1])


if __name__ == "__main__":
    Solution().process()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值