hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)
题意:
此题意很好理解,便不在此赘述;
题解:
解题思路: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)的更多相关文章
- KMP解决字符串最小循环节相关问题
经典问题 : 给出一个由某个循环节构成的字符串,要你找出最小的循环节,例如 abababab 最小循环节当是 ab ,而类似 abab 也可以成为它的循环节,但并非最短. 分析 : 对于上述问题有两个 ...
- HDU 4333 Revolving Digits 扩张KMP
标题来源:HDU 4333 Revolving Digits 意甲冠军:求一个数字环路移动少于不同数量 等同 于的数字 思路:扩展KMP求出S[i..j]等于S[0..j-i]的最长前缀 推断 nex ...
- 【扩展kmp+最小循环节】HDU 4333 Revolving Digits
http://acm.hdu.edu.cn/showproblem.php?pid=4333 [题意] 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的 ...
- HDU - 4333 Revolving Digits(拓展kmp+最小循环节)
1.给一个数字字符串s,可以把它的最后一个字符放到最前面变为另一个数字,直到又变为原来的s.求这个过程中比原来的数字小的.相等的.大的数字各有多少. 例如:字符串123,变换过程:123 -> ...
- 扩展KMP - HDU 4333 Revolving Digits
Revolving Digits Problem's Link Mean: 给你一个字符串,你可以将该字符串的任意长度后缀截取下来然后接到最前面,让你统计所有新串中有多少种字典序小于.等于.大于原串. ...
- KMP求字符串最小循环节
证明1: 对于一个字符串S,长度为L,如果由长度为len的字符串s(字符串s的最小循环节是其本身)循环k次构成,那么字符串s就是字符串S的最小循环节 那么字符串有个很重要的性质和KMP挂钩,即 i ...
- HDU 4333 Revolving Digits
扩展KMP的应用 我们发现本题的关键在于如何高效的判断两个同构字符串的大小关系,想到如果能够预处理出每一个同构字符串与原字符串的最长公共前缀,那么直接比较它们不一样的部分就好,扩展KMP正好是用来处理 ...
- poj 2406 Power Strings【字符串+最小循环节的个数】
Po ...
- 字符串(扩展KMP):HDU 4333 Revolving Digits
Revolving Digits Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
随机推荐
- delphi 子窗体只能最小化不能关闭的解决方案
cnpack下载地址:http://www.cnpack.org/showdetail.php?id=726&lang=zh-cn 时候创建的子窗体不能关闭,点关闭按钮时子窗体最小化了. 出现 ...
- vim的几个常用操作
现在很少会有人用vim来写代码,所以vim更常用在server上面编辑配置文件或者少量代码编辑: vim操作命令非常之多,如果仅用作一个配置文件的编辑器,掌握几个常用的操作就够了: 常用的操作其实就是 ...
- JQuery跳出each循环的方法(包含数组遍历)
0. 前言 也许我们通过 jquery 的循环方法进行数组遍历,但是当不符合条件时,怎么跳出当前循环?(即用each方法内,当不满足条件时想break跳出循环体,想continue继续执行下一个循环遍 ...
- CentOS 7 vi编辑命令
用vi打开一个yum文件 vi /usr/bin/yum 按 i 键后 进入insert模式,进入insert模式后才能进行修改 修改完成后 按esc键进入command模式, 然后:wq 保存文件 ...
- [oracle] to_date() 与 to_char() 日期和字符串转换
to_date("要转换的字符串","转换的格式") 两个参数的格式必须匹配,否则会报错. 即按照第二个参数的格式解释第一个参数. to_char(日期,& ...
- react 粗略使用
1.首先在index.html页面上写好dom,给他一个id让他引用js里的react. 2.index.js里面的代码就是三步走. 第一步:引用react,各种引用依赖. 第二步:创建dom,但它是 ...
- 1.ansible基本参数介绍
想使用ansible 先--help学习下基本的options吧小兄弟1: -m 指定模块名称只有一个模块command 可以省略:-M 指出模块路径来加载2: -a 指定模块参数就是模块的内容你知道 ...
- moogodb 安装及简单介绍
1,安装Moogodb 因为是windows 64位操作系统,直接到官网上下载.msi文件,下载完成后点击安装,点击同意协议之后,出现下面的对话框, Choose Setup Type, 就是选择安装 ...
- bzoj2762-[JLOI2011]不等式组
求 \(x=k\) 时满足一元一次不等式 \(ax+b<c\) 的个数. 解出 \(\frac{c-b}{a}\) 之后取整,得到合法区间,用树状数组维护. 注意 \(a\) 的值域是 \([- ...
- RESTful 架构详解
RESTful 架构详解 分类 编程技术 1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次 ...