1.题目描述
一个字符串的前缀是从第一个字符开始的连续若干个字符,例如 abaab 共有 55 个前缀,分别是 a,ab,aba,abaa,abaab。
我们希望知道一个 N 位字符串 S 的前缀是否具有循环节。
换言之,对于每一个从头开始的长度为 i(i>1)的前缀,是否由重复出现的子串 A 组成,即 AAA…A (A 重复出现 K 次,K>1)。
如果存在,请找出最短的循环节对应的 K 值(也就是这个前缀串的所有可能重复节中,最大的 K值)。
输入格式
输入包括多组测试数据,每组测试数据包括两行。
第一行输入字符串 S 的长度 N。
第二行输入字符串 S。
输入数据以只包括一个 0 的行作为结尾。
输出格式
对于每组测试数据,第一行输出 Test case # 和测试数据的编号。
接下来的每一行,输出具有循环节的前缀的长度 i 和其对应 K,中间用一个空格隔开。
前缀长度需要升序排列。
扫描二维码关注公众号,回复:
15426384 查看本文章
在每组测试数据的最后输出一个空行。
数据范围
2≤N≤1000000
输入样例:
3
aaa
4
abcd
12
aabaabaabaab
0
输出样例:
Test case #1
2 2
3 3
Test case #2
Test case #3
2 2
6 2
9 3
12 4
2.思路解析
不清楚kmp的可以先看下这篇
https://blog.csdn.net/m0_68055637/article/details/128596380
首先发现一个性质,对字符串S自己匹配求出next数组,然后根据定义,对于每一个i,s[i-next[i]+1~i]与s[1~next[i]]是相等的,而且不存在更大的next值满足条件.
既然如此的话,那么我们发现当i-next[i]能够整除i时候,那么s[1~i-next[i]]就是s[i-1]的最小循环元,至于次数那么也就是i/(i-next[i]).
3.代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
num=0;
while(true){
int n=sc.nextInt();
if(n==0){
break;
}
String str1=sc.next();
num++;
System.out.println("Test case #"+num);
shuchu(n,str1);
System.out.println();
}
}
public static int num;
public static void shuchu(int n,String str){
//KMP的next的数组
int[]next=new int[n];
//此处next的数组是使用最大长度表实现 跟next[0]=-1意义不同
next[0]=0;
for (int i = 1,j=0; i <n ; i++) {
while(j>0 &&str.charAt(j)!=str.charAt(i)){
j= next[j-1];
}
if(str.charAt(i)==str.charAt(j)){
j++;
}
next[i]=j;
if(next[i]!=0){
//i+1是当前字符串的长度 i+1-next[i]是这个字符串重复的子串的长度
if((i+1)%(i+1-next[i])==0){
int temp=(i+1)/(i+1-next[i]); //个数
System.out.println((i+1)+" "+temp);
}
}
}
}
}
感谢你能看完, 如有错误欢迎评论指正,有好的思路可以交流一波,如果对你有帮助的话,点个赞支持下