#POJ 1961 Period (KMP)

本文探讨了如何使用KMP算法解决周期字符串问题,通过分析字符串的前缀和后缀特性,实现高效判断和输出字符串中所有可能的周期及其长度。

Description

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 <= i <= 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 AK ,that is A concatenated K times, for some string A. Of course, we also want to know the period K.

Input

The input consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – 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

题目大意 : 输入一个字符串, 输出一个下标I和一个长度N, 表示该I之前的字符串可以被分成N个相同的字符串

思路 : KMP算法中的getnext操作, 可以将字符串的每个下标对应的在他之前最大  前缀与后缀相同  的长度表示出来,从而使两个字符串匹配时可以跳过匹配一定失败的地方,不过这道题不需要匹配操作, 我们只需要知道他getnext后数组内的值就好了。因为我们是从头开始开始遍历的,所以每次的i - nxt[i]的值就是这个循环体的长度, 那么如果总长度 % 循环题长度 == 0的话, 就说明是可以循环的,但是题目又说了 K > 1,  也就是当循环体长度为1的不考虑,打个比方 : 如果你输入 “  a  ”, 正确答案应该是什么也不输出, 但是你直接按刚才那样的方法, 他会输出 1 1, 换个例子, 如果你输入 "abcd", 正确答案应该也是啥也不输出, 但是 他会输出  “ 1 1, 2 1, 3 1 , 4 1 ”, 所以我们要加个条件 !出错的原因在于你把单个字符也当作循环节了, 而单个字符如果没有其他前缀与后缀相同的情况下,他的nxt值为0,也就是让 j 返回最开始的地方 (如果你已经理解了KMP, 这里说的应该能看懂, 不理解的话先看下其他大佬的KMP解释把 ^_^)

注意输出格式, 每个样例后有一个空行~

PS : 语文水平有限。。。如果KMP已经会了还是不明白, 可以试试写下这道题:http://poj.org/problem?id=2406

本题就是POJ2406的循环而已。

AC代码 :

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1e6 + 5;

char str[maxn];
int nxt[maxn], n, X;
void getnext() {
    nxt[0] = -1;
    int i = 0, j = -1;
    while (i < n) {
        if (j == -1 || str[i] == str[j])
            nxt[++i] = ++j;
        else
            j = nxt[j];
    }
}

int main()
{
    while (scanf("%d", &n) && n) {
        memset(nxt, 0, sizeof(nxt));
        scanf("%s", str);
        getnext();
        cout << "Test case #" << ++X << endl;
        for (int i = 1; i <= n; i++) {
            if (i % (i - nxt[i]) == 0 && i != (i - nxt[i]))  //防止单个字符作为循环体
                cout << i << " " << i / (i - nxt[i]) << endl;
        }
        cout << endl;
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值