1. 程式人生 > >「UVA1328」「POJ1961」 Period 解題報告

「UVA1328」「POJ1961」 Period 解題報告

UVA1328 Period

其他連結:luogu UVA1328 POJ1961

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (\(2 \le i \le N\))we want to know the largest \(K > 1\)

(if there is one) such that the prefix of \(S\) with length i can be written as \(A^K\), that is A concatenated K times, for some string A. Of course, we also want to know the period K.

Input

The input file consists of several test cases. Each test case consists of two lines. The first one contains \(N\)

(\(2 \le N \le 1000000\)) the size of the string \(S\). The second line contains the string S. The input file ends with a line, having the number zero on it.

Output

For each test case, output ‘Test case #’ and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.

Sample Input

3
aaa
12
aabaabaabaab
0

Sample Output

Test case #1
2 2
3 3
Test case #2
2 2
6 2
9 3
12 4

翻譯

(louhc自己翻譯的啦 luogu ID Sinner也是我)

題意描述

對於給定字串S的每個字首,我們想知道它是否為週期串。也就還是說,它是否為某一字串重複連線而成(必須至少重複2次)(即迴圈節)。

輸入

多組資料。每組資料,第一行一個數字表示長度,第二行一個字串S。

輸出

輸出字首長度與迴圈節數量。

說明

字串長度不超過1000000,僅由小寫字母組成。
對於每個字首,只要輸出長度最小的迴圈節


寫在前面

Substr(i, j)表示s的子串s[i~j]

這裡s的下標從1開始

i的上一個匹配:一個位置j,滿足Substr(1, j) == Substr(i - j + 1,N)

下面黑線表示字串,其中紅框中包含的字元相等(這是自然,同一個字串嘛)。

j還要滿足

(注意啦 兩條黑線表示同一個字串,只是位置不同)

(其實這也算是KMP的複習吧。。。)

下面圖中紅框中都表示相同

演算法

KMP。由於這不是KMP學習筆記,不仔細講,請先學會KMP。

思路

這題也可算是“拍大腿”系列了吧?其實你看看下面程式碼,真的很簡單,關鍵就是如何推出這個結論。

(我不用next,用了f做陣列名,希望大家不要看不習慣,意思是一樣的)

粉色部分也表示相同。這很明顯,因為字元是一一對應的嘛(同一個字串位置相同、長度相同的字串當然一樣)。

由於紅框內完全相同,還可以——

繼續對應!灰線表示在原字串中是對應的。

還可以對應!

可能就會出現這樣的情況!(當然可能最前面不夠長度)

因此,只要f[i]>0,i前面肯定有迴圈節!(只不過不知道是否完整,bb|abb|abb|abb這樣也看作是)而且迴圈節長度為i - f[i] + 1!。因此只要判斷迴圈節長度能否將長度整除即可。具體請見程式碼(真的短)。

程式碼

#include<cstdio>
using namespace std;
#define MAXN 1000005

int N, T;
char s[MAXN];
int f[MAXN];

int main(){
    while( ~scanf( "%d", &N ) && N ){
        scanf( "%s", s + 1 ); T++;
        printf( "Test case #%d\n", T );
        int t(0); f[1] = 0;
        for ( int i = 2; i <= N; ++i ){
            while ( s[i] != s[t + 1] && t ) t = f[t];
            if ( s[i] == s[t + 1] ) t++;
            f[i] = t;
            if ( f[i] != 0 && i % ( i -  f[i] ) == 0 ) printf( "%d %d\n", i, i / ( i - f[i] ) );
        }
        putchar('\n');
    }
    return 0;
}