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()