鉴于以下问题:
定义:
设S是字母表上的字符串Σ。
S'
是S
的最小句点 如果S'
是最小的字符串,那么:
S = (S')^k (S'')
,其中
S''
是S
的前缀。如果不存在此类S'
,则S
为。{1}} 不是定期的。示例:
S = abcabcabcabca
。然后abcabc
是S = abcabc abcabc a
以来的一段时间,但自abc
起最短的时间段为S = abc abc abc abc a
。给出一个算法来查找输入字符串
S
或的最小周期 声明S
不是定期的。提示:您可以在
中执行此操作O(n)
...
我的解决方案:我们使用KMP,它在O(n)中运行。
通过问题的定义,S =(S')^ k(S''),那么我认为如果我们创造 最短时间内的自动机,并找到找到最短时间的方法,然后我就完成了。
问题是在哪里放置自动机的失败箭头......
非常感谢任何想法,
此致
答案 0 :(得分:0)
我不确定我理解您的尝试解决方案。 KMP是一个有用的子程序 - 最小的时期是KMP在完全匹配后移动针弦(即S
)的距离。
答案 1 :(得分:0)
这个问题可以用Z函数解决,this教程可以帮到你。
答案 2 :(得分:0)
查看此解决方案是否适用于O(n)。我使用了字符串旋转。
public static int stringPeriod(String s){
String s1= s;
String s2= s1;
for (int i=1; i <s1.length();i++){
s2=rotate(s2);
if(s1.equals(s2)){
return i;
}
}
return -1;
}
public static String rotate(String s1){
String rotS= s1;
rotS = s1.substring(1)+s1.substring(0,1);
return rotS;
}
完整的程序可在this github repository
中找到答案 3 :(得分:0)
好的,因此这个问题可以肯定地在O(n)中解决,我们只需要按照您的建议巧妙地使用KMP。
解决最长的适当前缀(这也是一个后缀问题)是我们将使用的KMP的重要组成部分。
最长的适当前缀(这也是一个后缀问题)是一个大问题,所以现在就将其称为前缀后缀问题。
前缀后缀问题可能很难理解,因此我将举一些例子。
“ abcabc”的前缀后缀解决方案是 “ abc”,因为这是最长的字符串,都是正确的前缀 和适当的后缀(正确的前缀和后缀不能是整个 字符串)。
“ abcabca”的前缀后缀解决方案是“ a”
Hmmmmmmmmm 如果我们只是从“ abcabca”结尾处截去“ a”,请等一会儿,然后留下“ abcabc”,如果我们得到新的解决方案(“ abc”)字符串并再次将其砍掉,剩下的就是“ abc” Hmmmmmmmmm 。非常有趣。(这几乎是解决方案,但我将讨论为什么如此)
好吧,让我们尝试进一步将这种直觉形式化,看看我们是否能够找到解决方案。
我将在论点中使用一个关键假设:
我们模式的最小周期是我们模式中每个较大周期的有效周期
让我们将模式的前i
个字符的前缀后缀解决方案存储在lps[i]
中。此lps
数组可以在O(n)
中进行计算,并且已在KMP算法中使用,您可以在https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/
很显然,我将列出一些lps
数组的示例
模式:“ aaaaa”
lps:[0、1、2、3、4]
模式:“ aabbcc”
lps:[0,1,0,0,0,0]
模式:“ abcabcabc”
lps:[0、0、0、1、2、3、4、5、6]
现在让我们定义一些变量,以帮助我们找出为什么这个lps
数组有用。
让
l
为模式的长度,让k
为lps数组(k=lps[l-1]
)中的最后一个值
值k
告诉我们,字符串的前k
个字符与字符串的后k
个字符相同。我们可以利用这一事实找到一个时期!
使用此信息,我们现在可以显示由字符串的前l-k
个字符组成的前缀形成有效期。这很清楚,因为我们如何定义k
数组,因此不在前缀中的后k
个字符必须与前缀中的前lps
个字符匹配。我们前缀中的前k
个字符必须与构成我们后缀的后k
个字符相同。
在实践中,您可以通过一个简单的while循环来实现此功能,如下所示,其中index
标记了您当前认为是最小周期的后缀的结尾。
public static void main(String[] args){
String pattern="abcabcabcabca";
int[] lps= calculateLPS(pattern);
//start at the end of the string
int index=lps.length-1;
while(lps[index]!=0){
//shift back
index-=lps[index];
}
System.out.println(pattern.substring(0,index+1));
}
由于计算lps
发生在O(n)
中,并且您总是在while循环中至少移回了1步,因此整个过程的时间复杂度仅为O(n)
如果您想在下面查看我的确切代码,我从我的calculateLPS()方法中大量借鉴了KMP的geeksForGeeks实现,但是我建议您也看一下它们的解释:https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/
static int[] calculateLPS(String pat) {
int[] lps = new int[pat.length()];
int len = 0;
int i = 1;
lps[0] = 0;
while (i < pat.length()) {
if (pat.charAt(i) == pat.charAt(len)) {
len++;
lps[i] = len;
i++;
}
else {
if (len != 0) {
len = lps[len - 1];
}
else {
lps[i] = len;
i++;
}
}
}
System.out.println(Arrays.toString(lps));
return lps;
}
最后但并非最不重要的一点,感谢您发布了这样一个有趣的问题,弄清楚它真有趣!另外,我对此还很陌生,所以请让我知道我的解释中是否有任何疑问。
答案 4 :(得分:0)
KMP
可以轻松解决此问题示例:
Original string = abaaba
n = 6
New string = abaabaabaaba
KMP values for this new string: 0 0 1 1 2 3 4 5 6 7 8 9
第一个值> = n为6,它位于位置8。8-6 + 1 = 3是字符串的最短时段(aba)的长度。