传送门

题意:

  此题意很好理解,便不在此赘述;

题解:

  解题思路:KMP求字符串最小循环节+拓展KMP

  ①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k;

  ②根据拓展KMP求出字符串s的nex[]数组,那么对于由第 i 位打头构成的新数b,如何判断其与原数a的大小关系呢?

    1)如果 i%k == 0,那么b == a;

    2)如果 i%k ≠ 0 ,令L=nex[i],那么只需判断s[ i+L ]与s[ L ]的大小关系即可,需要注意的是,如果i+L = len呢?此时又该怎样处理呢?

    方法1:依次判断s[0,1,....] 与 s[ L,L+1,..... ]的关系,直到找出第一个不相等的位置判断其大小;

    方法2:判断 s[ nex[L] ]与s[ L+nex[L] ]的大小关系;

  如果采用方法1,很不幸,会超时,所以,方法2才是行之有效的方法;

  根据题意,此题让求得是不同的数,那么,如何去重呢?

  根据KMP已经求出了k,那么串s得循环周期为 len / k ,那么每种新数必然会重复 len / k次,只要在输出结果上将求出的答案除以 (len/k) 即可;

  还有一点需要注意的是,和原数相同的数,当且仅当只有一个,不论输入任何数,输出1即可;

AC代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e6+; char digit[maxn];
int nex[maxn]; int Period()
{
int len=strlen(digit);
nex[]=-;
nex[]=;
int cnt=;
int index=;
while(index <= len)
{
if(digit[index-] == digit[cnt])
nex[index++]=++cnt;
else if(cnt != )
cnt=nex[cnt];
else
nex[index++]=;
}
int k=len;
if(len%(len-nex[len]) == && nex[len] != )
k=len-nex[len];
return k;
}
void getNext()
{
int len=strlen(digit);
nex[]=len;
int j=;
while(j+ < len && digit[j+] == digit[j])
j++;
nex[]=j;
int k=;
for(int i=;i < len;++i)
{
int p=k+nex[k]-;
int l=nex[i-k];
if(l < p-i+)
nex[i]=l;
else
{
j=max(,p-i+);
while(i+j < len && digit[i+j] == digit[j])
j++;
k=i;
nex[i]=j;
}
}
}
bool isLess(int i,int j,int len)
{
if(j == len)//如果j == len
{
j=nex[i];
i=i+nex[i];
}
return (digit[j]-'') < (digit[i]-'');
}
void Solve()
{
int k=Period();//KMP求出最小循环节的长度
getNext();//拓展KMP求解nex[] int ansL=;
int ansG=;
int len=strlen(digit);
for(int i=;i < len;++i)
{
int l=nex[i];
if(i%k == )//与原数相等
continue; if(isLess(l,i+l,len))//判断是否小于原数
ansL++;
else
ansG++;
}
printf(" %d %d %d\n",ansL/(len/k),,ansG/(len/k));
}
int main()
{
int test;
scanf("%d",&test);
for(int kase=;kase <= test;++kase)
{
scanf("%s",digit);
printf("Case %d:",kase);
Solve();
}
return ;
}

  在网上看的其他人写的题解,有个很巧妙的方法:

  将字符串s拷贝一份加入到字符串s中,通过拓展KMP求出nex[]后,对于由第 i 打头构成的新数b:

  1)如果nex[i] > len/2,那么b == a;

  2)判断s[ i+nex[i] ]与s[ nex[i] ]的相对大小;


分割线:2019.5.7

省赛临近,重新温习了一下拓展KMP

思路:

  定义串 s , t

  读入数据到 t 串,然后,复制两边 t 串到 s 串;

  以 s 为母串,t 为子串求解ext[]数组;

  遍历一遍串t,对于位置 i ,判断 t[ ext[i] ] 与 s[ i+ext[i] ] 的大小关系;

  前者大,L++,反之, G++;

  输出结果 L / k , 1 , G / k;

  k : 串 t 的循环节;

  之所以 / k 是因为可能有重复的;

代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e5+; char s[maxn];
char t[maxn]; struct KMP
{
int nex[maxn];///数组大小根据题意而定
void getNex(const char *s)
{
int len=strlen(s);
nex[]=-;
nex[]=;
int cnt=;
int index=;
while(index <= len)
{
if(s[index-] == s[cnt])
nex[index++]=++cnt;
else if(cnt != )
cnt=nex[cnt];
else
nex[index++]=;
}
}
int F(const char *s)///返回串s的循环节
{
getNex(s);
int len=strlen(s);
int res=;
if(nex[len] != && len%(len-nex[len]) == )
res=len/(len-nex[len]);
return res;///最小循环节长度 = len/res
}
}_kmp;
/**
拓展KMP
nex[i]:t[0,...m-1]与t[i,...,m-1]的最长公共前缀
ext[i]:s[i,...n-1]与t[0,...,m-1]的最长公共前缀
*/
struct ExtendKMP
{
int nex[maxn];
int ext[maxn];
void getNex(const char *t)///预处理出t串的nex
{
int len=strlen(t);
nex[]=len;
nex[]=;
for(int i=;i < len && t[i] == t[nex[]];i++,nex[]++);
int K=;
for(int i=;i < len;++i)
{
int L=nex[i-K];
nex[i]=min(L,max(K+nex[K]-i,));///K+nex[K]-i 可能小于0,所以两者取max,整体取min
for(int j=i+nex[i];j < len && t[j] == t[nex[i]];j++,nex[i]++);
if(K+nex[K] < i+nex[i])
K=i;
}
}
void getExtend(const char *s,const char *t)
{
int n=strlen(s);
int m=strlen(t);
ext[]=;
for(int i=;i < n && i < m && s[i] == t[i];i++,ext[]++);
int K=;
for(int i=;i < n;++i)
{
/**
P=K+ext[K]-1,最右边界
s[K,...,P] = t[0,.....,P-K]
s[i,...,P] = t[i-K,...,P-K]
t[i-K,....,i-K+L-1] = t[0,.......L-1]
*/
int L=nex[i-K];
ext[i]=min(L,max(K+ext[K]-i,));
for(int j=i+ext[i];j < n && ext[i] < m && s[j] == t[ext[i]];j++,ext[i]++);
if(K+ext[K] < i+ext[i])
K=i;
}
}
}_eKMP; void Solve()
{
_eKMP.getNex(t);
_eKMP.getExtend(s,t); int L=,G=;
int len=strlen(t);
for(int i=;i < len;++i)
{
int ext=_eKMP.ext[i];
if(ext == len)
continue;
if(s[i+ext] > t[ext])
G++;
else
L++;
}
int k=_kmp.F(t);///串t的循环节
printf("%d 1 %d\n",L/k,G/k);
}
int main()
{
int test;
scanf("%d",&test);
for(int kase=;kase <= test;++kase)
{
scanf("%s",t);
strcpy(s,t);
strcat(s,t);///拷贝两边t串到s
s[strlen(t)<<]='\0';///可加可不加,最好加上
printf("Case %d: ",kase);
Solve();
}
return ;
}

hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)的更多相关文章

  1. KMP解决字符串最小循环节相关问题

    经典问题 : 给出一个由某个循环节构成的字符串,要你找出最小的循环节,例如 abababab 最小循环节当是 ab ,而类似 abab 也可以成为它的循环节,但并非最短. 分析 : 对于上述问题有两个 ...

  2. HDU 4333 Revolving Digits 扩张KMP

    标题来源:HDU 4333 Revolving Digits 意甲冠军:求一个数字环路移动少于不同数量 等同 于的数字 思路:扩展KMP求出S[i..j]等于S[0..j-i]的最长前缀 推断 nex ...

  3. 【扩展kmp+最小循环节】HDU 4333 Revolving Digits

    http://acm.hdu.edu.cn/showproblem.php?pid=4333 [题意] 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的 ...

  4. HDU - 4333 Revolving Digits(拓展kmp+最小循环节)

    1.给一个数字字符串s,可以把它的最后一个字符放到最前面变为另一个数字,直到又变为原来的s.求这个过程中比原来的数字小的.相等的.大的数字各有多少. 例如:字符串123,变换过程:123 -> ...

  5. 扩展KMP - HDU 4333 Revolving Digits

    Revolving Digits Problem's Link Mean: 给你一个字符串,你可以将该字符串的任意长度后缀截取下来然后接到最前面,让你统计所有新串中有多少种字典序小于.等于.大于原串. ...

  6. KMP求字符串最小循环节

    证明1: 对于一个字符串S,长度为L,如果由长度为len的字符串s(字符串s的最小循环节是其本身)循环k次构成,那么字符串s就是字符串S的最小循环节 那么字符串有个很重要的性质和KMP挂钩,即  i ...

  7. HDU 4333 Revolving Digits

    扩展KMP的应用 我们发现本题的关键在于如何高效的判断两个同构字符串的大小关系,想到如果能够预处理出每一个同构字符串与原字符串的最长公共前缀,那么直接比较它们不一样的部分就好,扩展KMP正好是用来处理 ...

  8. poj 2406 Power Strings【字符串+最小循环节的个数】

                                                                                                      Po ...

  9. 字符串(扩展KMP):HDU 4333 Revolving Digits

    Revolving Digits Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

随机推荐

  1. 集合之TreeMap(含JDK1.8源码分析)

    一.前言 前面所说的hashMap和linkedHashMap都不具备统计的功能,或者说它们的统计性能的时间复杂度都不是很好,要想对两者进行统计,需要遍历所有的entry,时间复杂度比较高,此时,我们 ...

  2. python数学第八天【协方差】

  3. delphi 怎么实现主窗口退出时,有一个提示框?

    无论点窗口上的[按钮]还是[右上角的叉],能出现一个提示窗口,“是”-退出窗口,“否”-重新登录(调出登录窗口),“取消”-返回.MessageBox能实现吗?还是要调用新窗口(我调用窗口,有些错误) ...

  4. vue axios 封装(三)

    封装三: import axios from 'axios' import { Message, MessageBox } from 'element-ui' import store from '. ...

  5. Socket初见

    前端代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <t ...

  6. 【python练习题】程序2

    2.题目:企业发放的奖金根据利润提成.利润(I)低于或等于10万元时,奖金可提10%:利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%:20万到40 ...

  7. KKT条件

    kkt条件背下来容易.理解上还有问题 主要是lambda≥0和lambda*f(x)=0这两个条件懵逼. 下面说明一下为什么 参考:https://blog.csdn.net/newthinker_w ...

  8. JS实现控制HTML5背景音乐播放暂停

    首先在网页中嵌入背景音乐,html5代码为: <script src="http://wuover.qiniudn.com/jquery.js"></script ...

  9. 思路:当一个表嵌套另一个表时候 只需在dao中引入该mapper即可 进行正常的数据插入 查询 修改等

  10. 我踩过的Alwayson的坑!

    最近被sql server Alwayson高可用组和读写分离,弄得神魂颠倒,身心俱疲.遇到了下面一些问题,提醒自己也给后来人做些记录. EntityFramework支不支持Alwayson? 起因 ...