传送门

题意:

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

题解:

  解题思路: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. 四、Mysql主从同步

    一.MySQL Replication介绍 MySQL Replication 官方文档 Replication可以实现将数据从一台数据库服务器(master)复制到一或多台数据库服务器(slave) ...

  2. PDO访问Mysql数据库

    $dsn = 'mysql:host=127.0.0.1;dbname=myblog'; $username = 'root'; $pwd = '; $pdo = new PDO($dsn,$user ...

  3. LODOP直接用base64码输出图片

    Lodop中的ADD_PRINT_IMAGE,也可以直接输出base64码图片,不用加img标签,如果加了img标签,会被当做超文本对待,受浏览器引擎解析的影响. 什么时候使用base64码直接输出比 ...

  4. How to enable flash on Chromium

    sudo apt install chromium-browser pepperflashplugin-nonfree

  5. sed命令参数之-r -i

    对于初学linux的朋友来说,能记住命令附带的一大帮参数就以及非常不容易了.好不容易把该用的参数都想全了.sed -irns 后面一大片脚本 ,一执行出错了 what!!!! 创建一下测试环境 hea ...

  6. 当页面是动态时 如果后台存储id可以通过查询后台方式获取对象;当后台没有存储时候 只有通过前端标记了 例如标记数量为10 我们根据传递过来的10循环取值

    当页面是动态时 如果后台存储id可以通过查询后台方式获取对象;当后台没有存储时候 只有通过前端标记了 例如标记数量为10 我们根据传递过来的10循环取值

  7. [西安交大附中集训] d6 删边(cip)

    B. 删边(cip.cpp/in/out 1S/256M) 题面 给出一个没有重边和自环的无向图,现在要求删除其中两条边,使得图仍然保持连通. 你的任务是计算有多少组不合法的选边方案.注意方案是无序二 ...

  8. DRF 版本 认证

    DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢大家都知道我们开发项目是有多个版本的 当我们项目越来越更新~版本就越来越多我们不可能新的版本出了~以前旧的版本 ...

  9. Spring 使用介绍(九)—— 零配置(二)

    三.Bean定义 1.开启bean定义注解支持 开启注解支持须添加以下配置项: <context:component-scan base-package="cn.matt"/ ...

  10. python 机械学习之sklearn的数据正规化

    from sklearn import preprocessing    #导入sklearn的处理函数用于处理一些大值数据 x_train, x_test, y_train, y_test = tr ...