传送门

题意:

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

题解:

  解题思路: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. python数据结构与算法第五天【顺序表】

    1.列表存储的两种方式 (1)元素内置方式 采用元素内置的方式只能存放同类型元素的数据类型,例如列表中的元素都为整形,元素类型相同,每个元素存放的地址空间大小也相同,则列表中每个元素都是顺序存放的 ( ...

  2. Yii2后台管理系统常规单据模块最佳实践

    后台管理系统的常规单据通常包括数据,页面,功能:其中数据,页面,功能又可以细分如下: 分类  二级分类  主要内容  注意事项  例如 数据 数据库迁移脚本  用于数据表生成及转态回滚 1.是否需要增 ...

  3. QTP自动化测试-点滴-步骤

    1 添加 test 2 设置 整个测试项目的 setting -数据表位置 3 添加 引用 方法文件 4 添加 action 5 添加 action 对应的 repository 控件库 6 录制.整 ...

  4. Java多线程之synchronized线程锁

    package org.study2.javabase.ThreadsDemo.sync; /** * @Auther:GongXingRui * @Date:2018/9/18 * @Descrip ...

  5. 提示“Web打印服务CLodop未安装启动”的各种原因和解决方法

    旧版提示:"CLodop云打印服务(localhost本地)未安装启动!"新版提示:"Web打印服务CLodop未安装启动,点击这里下载执行安装(若此前已安装过,可点这里 ...

  6. MySQL in型子查询陷阱

    现在有两个表,table1和table2,table1有1千万数据(id 主键索引),table2有三条数据(uid字段 3,5,7): select * from table1 where id i ...

  7. Socket初见

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

  8. Announcing Windows Template Studio in UWP

    今天,我们很高兴地宣布您的文件→新的通用Windows平台应用程序在Visual Studio - Windows模板工作室中的下一个演变.Windows Template Studio在开发人员调查 ...

  9. Asp.Net Core 输出 Word

    In one of the ASP.NET Core projects we did in the last year, we created an OutputFormatter to provid ...

  10. 基准对象object中的基础类型----列表 (四)

    object有如下子类: CLASSES object basestring str unicode buffer bytearray classmethod complex dict enumera ...