题解 UVA10298 【Power Strings】
此题我写的是后缀数组SA解法,如果不会后缀数组的可以跳过本篇blog了。
参考文献:罗穗骞 2009集训队后缀数组论文
前记
最近学后缀数组,肝了不少题,也分出了后缀数组的几个题型,看这题没有后缀数组的解法,于是我决定来水一波。
注:思想正确,代码不一定正确。
分析题意
给定一个字符串 L,已知这个字符串是由某个字符串 S 重复 R 次而得到的,
求 R 的最大值。
其实就是求字符串L的连续重复子串。连续重复子串就是后缀数组的一个题型。
算法分析
1、我们需要最大的R,就说明我们需要连续重复子串的子串长度最小,那我们就可以首先枚举子串的长度k。
2、如何判断枚举出的子串是否符合题意,为L的连续重复子串。相信学过后缀数组的大家一定知道如何求最长公共前缀,我在这里简要提一下。
最长公共前缀
定义&&性质
一、
Suf(i)为字符串S的从i位开始的后缀。
例:S="LAHAKIOI"
Suf(0)="LAHAKIOI".Suf(4)="AKIOI".(字符串从0开始)
二、
height[i]为Suf(SA[i])和Suf(SA[i-1])的最长公共前缀

三、
h[i]=height[rank[i]],也就是 Suf(i)和在它前一名的后缀的最长公共前
缀。
h数组有以下性质:
h[i]≥h[i-1]-1
具体证明这里不给出,如果有需要可以看我blog里的后缀数组全讲。
四、
Suf(j)和Suf(k)的最长公共前缀为(height[rank[j]+1],height[rank[j]+2],height[rank[j]+3],……,height[rank[k]])的最小值。
具体证明依然不给出,有需要可以看我的blog。
正所谓我们使用后缀数组求出了SA数组和Rank数组,我们想要求最长公共前缀,就要高效求出height数组。
求height数组,我们可以根据h数组的性质(三),对于每一个后缀和他上一个的字符串,我们不需要从头开始寻找他们的最长公共前缀,这样时间复杂度高达O(n^2),我们可以直接从h[i-1]-1处开始判断,因为根据h数组的性质,这两个字符串的前h[i-1]-1位一定是相同的。
这样时间复杂度降到O(n).
这里可能大家看的不是很懂,不过代码比较简单,比较容易懂:
void get_height(){ //求height数组
int k=0;
for(int i=0;i<n;i++){
if(k)k--; //h数组可以直接用一个计数器代替
int j=sa[rank[i]-1];
if(rank[i]-1==-1)continue; //因为我的排名从0开始,所以排名是0时要特判
while(s[i+k]==s[j+k])k++; //从k处找最长公共前缀
height[rank[i]]=k; //记录height数组
}
}
那我们求出了最长公共前缀就可以判断长度为k的子串是否满足为连续最长子串。
判断长度为k的子串是否满足为连续最长子串
1、首先,我们要判断字符串长度n是否能整除k,如果不能就显然不行。
2、再看Suf(0)到Suf(k)的最长公共前缀是否为n-k。前面根据性质四,我们可以知道Suf(0)到Suf(k)的最长公共前缀,但每一次的时间都为O(n),整体下来时间复杂度十分高。
但我们可以看到Suf(0)是固定的,所以我们可以预处理出只需求出 height 数组中的每一个数到height[rank[0]]之间的最小值记为ans数组即可。
全部代码:
#include<string>
#include<stdio.h>
#include<string.h>
#include<iostream>
#define maxn 300001
using namespace std;
char s[maxn];
int n,sa[maxn],rank[maxn],newRK[maxn],key2[maxn],sum[maxn],height[maxn],k;
int level;
void get_sum(int m){
for(int i=0;i<=m;i++) sum[i]=0;
for(int i=0;i<n;i++) sum[rank[i]]++;
for(int i=0;i<m;i++) sum[i]+=sum[i-1];
}
bool cmp(int x,int y,int L){
if(rank[x]!=rank[y])return false;
if((x+L>=n&&y+L<n)||(x+L<n&&y+L>=n))return false;
if(x+L>=n&& y+L>=n) return true;
return rank[x+L] == rank[y+L];
}
void get_height(){ //求height数组
int k=0;
for(int i=0;i<n;i++){
if(k)k--; //h数组可以直接用一个计数器代替
int j=sa[rank[i]-1];
if(rank[i]-1==-1)continue; //因为我的排名从0开始,所以排名是0时要特判
while(s[i+k]==s[j+k])k++; //从k处找最长公共前缀
height[rank[i]]=k; //记录height数组
}
}
void Suffix_Sort(){ //SA板子
for(int i=0;i<n;i++) rank[i]=s[i];
get_sum(356);
for(int i=n-1;i>=0;i--)
sa[--sum[rank[i]]]=i;
int w=1,m=max(n,356);
while(w<n){
int p=0;
for(int i=n-w;i<n;i++)key2[p++]=i; //第二关键字越界排前
for(int i=0;i<n;i++)if(sa[i]>=w)key2[p++]=sa[i]-w;//如果当前长度有第一关键字就记录
//以上按第二关键字排序
get_sum(m);
for(int i=n-1;i>=0;i--){
int j=key2[i];
sa[--sum[rank[j]]]=j;
}
//以上按第一关键字排序,直接覆盖之前的sa数组,不需要再开一个key1
newRK[sa[0]]=0;
level=1;
for(int i=1;i<n;i++){
if(cmp(sa[i-1],sa[i],w))
newRK[sa[i]]=level-1;
else
newRK[sa[i]]=level++;
}
for(int i=0;i<n;i++)
rank[i]=newRK[i];
//以上计算长度2*w的rank数组
if (level==n)break;
w<<=1;
}
}
int main(){
while(1){
int ans[maxn];
memset(sa,0,sizeof(sa));
memset(height,0,sizeof(height));
memset(ans,0,sizeof(ans));
memset(rank,0,sizeof(rank));
scanf("%s",s);n=strlen(s);
if(s[0]=='.')break;
Suffix_Sort();get_height();
//下面求ans数组
int a=rank[0],minx=10000000;
for(int i=a;i>=1;i--){
minx=min(minx,height[i]);
ans[sa[i-1]]=minx;
}
minx=10000000;
for(int i=a+1;i<=n;i++){
minx=min(minx,height[i]);
ans[sa[i]]=minx;
}
for(int i=1;i<=n;i++){
if(n%i==0){
if(ans[i]==n-i)
{
printf("%d\n",n/i);
break;
}
}
}
}
}
谢谢观赏
题解 UVA10298 【Power Strings】的更多相关文章
- UVA10298 Power Strings
UVA10298 Power Strings hash+乘法逆元+一点点数学知识 我们用取余法算出主串的hash,然后从小到大枚举子串的长度 显然,如果若干个子串的复制的hash值之和等于主串的has ...
- UVA10298 Power Strings [KMP]
题目传送门 Power Strings 格式难调,题面就不放了. 一句话题意,求给定的若干字符串的最短循环节循环次数. 输入样例#1: abcd aaaa ababab . 输出样例#1: 1 4 3 ...
- 洛谷 UVA10298 Power Strings 题解
Analysis 结论:设字符串长度为n,最长相同前后缀的长度为kmp[i],如n%(n-kmp[n])=0,则答案为n/(n-kmp[n]),否则为1. 如果循环节多于一个,以前n-kmp[n]个为 ...
- 【题解】Power Strings
题目描述 给定若干个长度小于等于10^6的字符串,询问每个字符串最多由多少个相同的子串重复连接而成.如:ababab,最多由3个ab连接而成. 输入输出格式 输入格式 若干行,每行一个字符串. 当读入 ...
- 「UVA10298」 Power Strings(KMP
题目描述 PDF 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例#1: 复制 abcd aaaa ababab . 输出样例#1: 复制 1 4 3 题解 Luogu的题解 这里是对目前 ...
- Power Strings[poj2406]题解
Power Strings Description - Given two strings a and b we define ab to be their concatenation. For ex ...
- Power Strings(kmp妙解)
Power Strings Time Limit : 6000/3000ms (Java/Other) Memory Limit : 131072/65536K (Java/Other) Tota ...
- poj2406 Power Strings (kmp 求最小循环字串)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 47748 Accepted: 19902 ...
- 【poj 2406】Power Strings 后缀数组DC3模板 【连续重复子串】
Power Strings 题意 给出一个字符串s,求s最多由几个相同的字符串重复而成(最小循环节的重复次数) 思路 之前学习KMP的时候做过. 我的思路是:枚举字符串的长度,对于当前长度k,判断\( ...
- POJ 2406 Power Strings (KMP)
Power Strings Time Limit: 3000MSMemory Limit: 65536K Total Submissions: 29663Accepted: 12387 Descrip ...
随机推荐
- 常见排序的Java实现
插入排序 1.原理:在有序数组中从后向前扫描找到要插入元素的位置,将元素插入进去. 2.步骤:插入元素和依次和前一个元素进行比较,若比前一个元素小就交换位置,否则结束循环. 3.代码: package ...
- docker运行安装mysql postgres
安装mysql [root@host1 ~]# docker images -a REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/mysql 5.7 4d ...
- 策略模式,重构if-else
最近完成了我们公司的公众号开发,在微信消息路由选择的时候一开始都是用if-else 来判断,后面if-else月写越多显得十分的乱.在网上简单查了一下解决方法,果然有不少干货,感觉最经典最简洁的还是使 ...
- Update(Stage4):spark_rdd算子:第2节 RDD_action算子_分区_缓存:算子和分区
一.reduce和reduceByKey: 二.:RDD 的算子总结 RDD 的算子大部分都会生成一些专用的 RDD map, flatMap, filter 等算子会生成 MapPartitions ...
- WKWebView单个界面添加请求头
https://www.jianshu.com/p/14b9ea4bf1d4 https://github.com/Yeatse/NSURLProtocol-WebKitSupport/blob/ma ...
- js学习:基本数据类型
数据类型在 js 里面分为两个大类: 基本数据类型 引用数据类型 基本数据类型: 数值 number 各种意义上的数字:整数.小数.浮点数等 正数:100 负数:-100 浮点数,小数:1.234 进 ...
- selenium webdriver 模拟鼠标悬浮
/**模拟鼠标悬浮在某元素上 * @param driver * @param locator */ public static void moveToElement(WebDriver driver ...
- ssh访问ubuntu13.10
步骤: 首先确保网络连接是ok,网络连接方式"桥接“,手动配置 ip 192.168.1.9,和主机是同一网段 1.检查当前有没有安装openssh-server(已安装) 2. 安装ope ...
- GO判断输入
判断用户密码输入: package main import"fmt" func main(){ var a int var b int fmt.Printf("请输入密码 ...
- ES5-严格模式
在es5中可以开启一种严格模式的代码形式,开启方式是:将全局或者函数的第一条语句定义为:'use strict';. 如果浏览器不支持,会将其解析为一条普通语句,没有任何的副作用. 开启全局模式后会有 ...