Revolving Digits HDU - 4333(扩展kmp)

本文探讨了如何通过旋转数字串来找出所有可能生成的不同数字,并对比这些数字与原数的大小关系。采用扩展KMP算法求解Z函数,去除重复数字,确保每个旋转后的数字都是唯一的。代码实现详细,适合算法学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Revolving Digits HDU - 4333
为避免歧义,该博客字符串从0开始。

题意:

给你一串数字(最长1E5位)。每次把末尾数字移动到首位。求经过len次操作后,有多少不同数字小于/等于/大于原数字。


解题思路:

将数字复制一次,插入到原数字的末尾。
如123—>123123那么从新数字的第2位开始向后走len,得到的就是原数字经过这些操作可能得到的数字。

- 扩展kmp:

对新的数字求z[]
注意:如果某位z函数>原数字长度,纳闷该位z函数=原数字长度。因为数字的长度在操作中是不会变化的。

void get_z(int len){
    int l=0,r=0;
    MT(z,0);
    for (int i=1;i<len;i++){
        if (i<=r) z[i]=min(r-i+1,z[i-l]);
        while (i+z[i]-1<len&&s[z[i]]==s[i+z[i]]) z[i]++;
        if (i+z[i]-1>r) l=i,r=i+z[i]-1;
    }
    for (int i=1;i<len;i++) z[i]=min(z[i],len/2);
}
- 去掉重复的数字:

可以发现,如果要保证操作得到的数字都是不同的。那么最后的E只能=1.如果E!=1说明一共有E组数据。而我们只需要1组数据。所以让G,L,E分别除E就可以了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
//#pragma GCC optimize(3,"Ofast","inline")
#define LL long long
#define MT(a,b) memset(a,b,sizeof(a))
const int mod=1000007;
const int maxn=2e5+5;
const int ONF=-0x3f3f3f3f;
const int INF=0x3f3f3f3f;
int z[maxn];
char s[maxn];
int L,E,G;
void get_z(int len){
    int l=0,r=0;
    MT(z,0);
    for (int i=1;i<len;i++){
        if (i<=r) z[i]=min(r-i+1,z[i-l]);
        while (i+z[i]-1<len&&s[z[i]]==s[i+z[i]]) z[i]++;
        if (i+z[i]-1>r) l=i,r=i+z[i]-1;
    }
    for (int i=1;i<len;i++) z[i]=min(z[i],len/2);
}
void solve(char *str){
    int len=strlen(str);
    L=E=G=0;
    for (int i=0;i<len;i++) s[i]=str[i];
    for (int i=len;i<2*len;i++) s[i]=str[i-len];
    get_z(len*2);
    for (int i=1;i<=len;i++){
        if (z[i]==len) E++;
        else{
            if (s[i+z[i]]>s[z[i]]) G++;
            if (s[i+z[i]]<s[z[i]]) L++;
        }
    }
    return;
}
int main (){
    int T;
    scanf("%d",&T);for (int turn=1;turn<=T;turn++){
        scanf("%s",s);
        solve(s);
        L/=E,G/=E;
        E/=E;
        printf("Case %d: %d %d %d\n",turn,L,E,G);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值