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;
}