原题链接.

题目大意:

给出一个数字,不断把最后一个数字提到第一位后产生的数字和原数字的大小关系,输出每一种大小关系有多少个,重复的不记,前导0算作不同的。

题解:

先不管重复。

很好的思路是把原串copy一遍,自我匹配个exkmp。
假设其exkmp[i]>=len,则说明相等,否则只需要比较下一位即可。

其实不把原串copy也是可以的,详情见代码。

什么时候会有重复呢?

就是这个数字有完整的循环节的时候。

如果是123 123 1或123 123 12,显然是不会有重复的。

只有当是123 123 132时,才会有重复。可以求出最短循环节的个数,答案除以它就好了。

利用kmp的next数组可以求出循环节的个数。

循环节的长度等于n - next[n],个数是n / (n - next[n]),这个对残缺循环节也适用,当然求出的是最小循环节。

详细证明不是很会,但是想到了一个简略证明。

如果原串是AAAA,next[n]根据定义,肯定是AAA的长度。

如果是残缺的AAAB,B是A的一个前缀,next[n]就是AAB的长度。

Code:

#include<cstdio> 
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 1e5 + 5;

int T, n, next[N], sum[3];
char s[N];

void Get_next() {
    next[1] = n;
    int a = 1;
    while(a < n && s[a] == s[a + 1]) a ++;
    next[2] = a - 1, a = 2;
    fo(i, 3, n) {
        int p = a + next[a] - 1, l = next[i - a + 1];
        if(i + l - 1 >= p) {
            int j = p - i + 1 > 0 ? p - i + 1 : 0;
            while(i + j - 1 < n && s[j + 1] == s[i + j]) j ++;
            a = i, next[i] = j;
        } else next[i] = l;
    }
}

int main() {
    scanf("%d", &T);
    fo(ii, 1, T) {
        scanf("%s", s + 1); n = strlen(s + 1);
        Get_next();
        fo(i, 1, n) {
            if(next[i] == n - i + 1) next[i] = 1; else
            next[i] = s[next[i] + 1] > s[i + next[i]] ? 0 : 2;
        }
        sum[0] = sum[1] = sum[2] = 0;
        next[n + 1] = 1;
        fo(i, 1, n) {
            if(next[i] == 1) sum[2 - next[n - i + 2]] ++; else
            sum[next[i]] ++;
        }
        next[1] = 0; int j = 0;
        fo(i, 2, n) {
            while(j && s[j + 1] != s[i]) j = next[j];
            if(s[j + 1] == s[i]) j ++;
            next[i] = j;
        }
        int xun = n / (n - next[n]);
        printf("Case %d: %d %d %d\n", ii, sum[0] / xun, sum[1] / xun, sum[2] / xun);
    }
}
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐