poj_2406 kmp

题目大意

    给出一个字符串S,S可能是多个较短的字符串的重复连接。比如"ababab"为字符串"ab"重复三次的结果;"abc" 为"abc"重复一次的结果;"abababab"可能是"abab"重复两次,也可能是"ab"重复四次的结果。 
    对一个给定的字符串S,求出最大的k,使得它为某个子串A重复k次的结果。

题目分析

    有了kmp的next数组(存储某子串的最长相同前后缀的长度),可以在此基础上分析。字符串S的子串A越短,则重复次数越多。 
    (1)显然,对于一个最多含有K个重复的A构成的串S,S的最长相同前后缀S1为K-1个A重复的结果; 
    (2)若S的最长相同前后缀S1和S确定的剩余部分S-S1(记为子串B)不能通过多个连接起来形成S1或者S,那么S必定无法分解为多个子串相连接的结果。 
    证明性质(2),若S-S1(记为A)不能拼成S或者S1,而S可以由多个子串拼接而成。假设S由P(不是最大的)个相同的子串B构成,那么P-1个B连接起来一定为S的相同前后缀(不一定为最长相同前后缀),且该相同前后缀的长度必定大于 strlen(S)/2,则strlen(S1)必定大于strlen(S)/2,则S的最长相同前后缀在S中必定相交! 
    对于串S,若他的最长前后缀相交,则可以肯定 S 由K个(S-S1)拼接而成。(通过画图可以说明)

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#define MAX_WORD_LEN  1000005
#include<stdio.h>
#include<string.h>
int gNext[MAX_WORD_LEN];
char gWord[MAX_WORD_LEN];
int gLength[MAX_WORD_LEN];
void GenerateNext(const char* sub_str, int* next, int len){
	next[0] = 0;								//next[i]表示sub_str[0, i]中最长相同前后缀的长度
	for (int i = 1; i < len; i++){
		int j = next[i - 1];
		while (sub_str[j] != sub_str[i]){		//不断回溯,每次利用next数组来确定下一个需要判断的位置
			if (j == 0){
				j = -1;
				break;
			}
			j = next[j - 1];
		}
		if (j < 0)
			next[i] = 0;
		else
			next[i] = j + 1;
	}
}

int main(){
	while (scanf("%s", gWord) != EOF){
		if (strcmp(gWord, ".") == 0){
			break;
		}
		int len = strlen(gWord);
		GenerateNext(gWord, gNext, len);
		if (len % (len - gNext[len - 1]) == 0){
			printf("%d
", len / (len - gNext[len -1]));
		}
		else
			printf("1
");		
	}
	return 0;
}
原文地址:https://www.cnblogs.com/gtarcoder/p/4813794.html