传送门

题意:

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

题解:

  解题思路: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. 使用urllib2+re爬取web网站

    应用1,使用urllib2+re爬取淘宝网指定页面的所有图片

  2. 阿里云 ECS 安全组

    以前在案例云买的ECS我一般都是 连上 ssh,然后把网站文件拿上去 ,安装好需要的环境 然后就可以顺利的打开网站了,这次帮一个朋友买的阿里云ECS让我蒙了, 一切都准备好了 网站打不开 防火墙也检查 ...

  3. Oracle中保留两位小数

    在最近的项目开发中,有个业务需求是界面显示的数字需要保留两位小数,目前我想到的解决方法有两种: (1)在写SQL的时候,直接保留两位小数 (2)在java代码里面将查询出来的数进行格式化处理,保留两位 ...

  4. rmse均方根误差

    rmse=sqrt(sum((w-r).^2)/length(w))

  5. hibernate主配置文件中指定session与当前线程绑定

    配置一条属性 <property name="hibernate.current_session_context_class">thread</property& ...

  6. AMD直奔5nm!这一步棋下得妙

    AMD今年将推出采用7nm工艺的第二代EPYC霄龙.第三代Ryzen锐龙处理器,其中后者已经在CES 2019上公开首秀,性能追评i9-9900K,功耗则低得多. 虽然被称为“女友”的GlobalFo ...

  7. table-layui

    本文章为原创文章,转载请注明出处 html <div class="layui-btn-group tableBtn"> <button class=" ...

  8. IntelliJ IDEA 取消【import .;】星号导包

    Setting -- Editor -- Code Style -- Java -- Imports 在 在 具体数值自行填写,够大即可,截图如下:

  9. [BZOJ 2743] [HEOI 2012] 采花

    Description 萧芸斓是Z国的公主,平时的一大爱好是采花.今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 \(n\) 朵花,花有 \(c\) 种颜色(用整数 \ ...

  10. BZOJ3502PA2012Tanie linie&BZOJ2288[POJ Challenge]生日礼物——模拟费用流+链表+堆

    题目描述 n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. 输入 输出 样例输入 5 2 7 -3 4 -9 5 样例输出 13   根据 ...