HDU6661 Acesrc and String Theory【SA】
Acesrc and String Theory
Problem Description
Acesrc is a famous string theorist at Nanjing University second to none. He insists that we should always say an important thing k times. He also believes that every string that can be obtained by concatenating k copies of some nonempty string is splendid. So, he always teaches newcomers, ``String theory problems are important! String theory problems are important! ... String theory problems are important!"
Today, he wants to examine whether the newcomers remember his instruction. He presents a string consisting of lower case letters and asks them the number of splendid substrings of the presented string. No one can solve this problem, and they will be scolded for hours. Can you help them solve this problem?
Note that equal splendid substrings occurred in different positions should be counted separately
给出一个字符串\(s\),问\(s\)中有多少个子串是有\(k\)个相同串拼接而成的
有个暴力的做法,枚举循环节长度然后哈希,复杂度\(O(\frac{n^2}{k})\)
现在考虑\(O(n\log{n})\)的做法,同样是枚举循环节长度\(len\),然后我们枚举循环节的起始位置,假设现在起始位置是\(pos\),那么我们先找从\(pos\)开始的\(k-1\)个循环节,需要保证每个循环节\(lcp(pos,pos+i\cdot len)\ge len\)
现在有了\(k-1\)个循环节,位置从\(L(pos)\),到\(R(pos+(k-1)\cdot len-1)\),现在要找符合条件的\(k\)循环子串,我们只要知道\(L\)和\(R+1\)的最长公共前缀\(lcp\)和以\(R\)结尾和以\(L+1\)结尾的最长公共后缀\(lcs\),就能知道这个\(k-1\)循环节对答案的贡献,我们显然可以构造一个字符串\(s_{l-pre}s_{l-pre+1}\cdots s_l s_{l+1}\cdots s_r s_{r+1}\cdots s_{r+suf}\),其中\(pre+suf==len && pre\le lcs && suf\le lcp\),所以当\(lcs+lcp\ge len\)的时候,对答案的贡献是\(lcs+lcp-len+1\),注意左边界和右边界的处理情况,有点小细节,防止重复计算
由于枚举循环节,复杂度为调和级数\(O(\sum_{i=1}^{n}\frac{n}{i})=O(n\log n)\)
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 3e5+7;
using LL = int_fast64_t;
LL ret;
int n,K,rk[2][MAXN],sec[MAXN],c[MAXN],sa[2][MAXN],height[2][MAXN],ST[2][MAXN][20];
char s[MAXN],t[MAXN];
void SA(int m, char *s, int *rk, int *sa, int *height){
int *RK = rk, *SEC = sec;
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[RK[i]=s[i]]++;
for(int i = 1; i <= m; i++) c[i] += c[i-1];
for(int i = n; i >= 1; i--) sa[c[RK[i]]--] = i;
for(int k = 1; k <= n; k <<= 1){
int p = 0;
for(int i = n - k + 1; i <= n; i++) SEC[++p] = i;
for(int i = 1; i <= n; i++) if(sa[i]>k) SEC[++p] = sa[i]-k;
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[RK[SEC[i]]]++;
for(int i = 1; i <= m; i++) c[i] += c[i-1];
for(int i = n; i >= 1; i--) sa[c[RK[SEC[i]]]--] = SEC[i];
swap(RK,SEC);
p = 0;
RK[sa[1]] = ++p;
for(int i = 2; i <= n; i++) RK[sa[i]] = SEC[sa[i]]==SEC[sa[i-1]] and SEC[sa[i]+k]==SEC[sa[i-1]+k] ? p : ++p;
if(p==n) break;
m = p;
}
int k = 0;
for(int i = 1; i <= n; i++) rk[sa[i]] = i;
for(int i = 1; i <= n; i++){
if(rk[i]==1) continue;
if(k) k--;
int j = sa[rk[i]-1];
while(i+k<=n and j+k<=n and s[i+k]==s[j+k]) k++;
height[rk[i]] = k;
}
}
void build_ST(){
for(int i = 1; i <= n; i++){
ST[0][i][0] = height[0][i];
ST[1][i][0] = height[1][i];
}
for(int j = 1; (1<<j) <= n; j++){
for(int i = 1; (i+(1<<j))-1 <= n; i++){
ST[0][i][j] = min(ST[0][i][j-1],ST[0][i+(1<<(j-1))][j-1]);
ST[1][i][j] = min(ST[1][i][j-1],ST[1][i+(1<<(j-1))][j-1]);
}
}
}
int qmin(int tg, int L, int R){
int d = log2(R-L+1);
return min(ST[tg][L][d],ST[tg][R-(1<<d)+1][d]);
}
int lcp(int tg, int l, int r){
int rkl = rk[tg][l], rkr = rk[tg][r];
if(rkl>rkr) swap(rkl,rkr);
return qmin(tg,rkl+1,rkr);
}
void calc(int pos, int len){
for(int i = 1; i < K - 1; i++) if(lcp(0,pos,pos+i*len)<len) return;
int L = pos, R = L + (K-1) * len - 1;
int LCP = min(len,lcp(0,L,R+1));
int LCS = min(len-1,lcp(1,n+1-R,n+1-L+1));
if(LCP+LCS>=len) ret += LCP + LCS - len + 1;
}
void solve(){
ret = 0;
scanf("%d %s",&K,s+1);
n = strlen(s+1);
if(K==1){
printf("%I64d\n",1ll*n*(n+1)/2);
return;
}
for(int i = 1; i <= n; i++) t[i] = s[n+1-i];
SA(128,s,rk[0],sa[0],height[0]);
SA(128,t,rk[1],sa[1],height[1]);
build_ST();
for(int len = 1; len <= n; len++){
for(int i = 1; i <= n; i += len){
if(i+(K-1)*len-1>=n) break;
calc(i,len);
}
}
printf("%I64d\n",ret);
}
int main(){
int T;
for(scanf("%d",&T); T; T--) solve();
return 0;
}
HDU6661 Acesrc and String Theory【SA】的更多相关文章
- Microsoft SQL Server 【Windows 身份验证】和 【sa】都无法登录的解决方案
1.修改启动参数:打开[SQL Server 配置管理器(SQL Server Configuration Manager)]→右键[SQL Server(MSSQLSERVER)]属性→高级(Adv ...
- 557. Reverse Words in a String III【easy】
557. Reverse Words in a String III[easy] Given a string, you need to reverse the order of characters ...
- hdu 6661 Acesrc and String Theory (后缀数组)
大意: 求重复$k$次的子串个数 枚举重复长度$i$, 把整个串分为$n/i$块, 如果每块可以$O(1)$计算, 那么最终复杂度就为$O(nlogn)$ 有个结论是: 以$j$开头的子串重复次数最大 ...
- HDOJ3374 String Problem 【KMP】+【最小表示法】
String Problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- C++中int与string的相互转换【转】
一.int转string 1.c++11标准增加了全局函数std::to_string: string to_string (int val); string to_string (long val) ...
- 213. String Compression【easy】
Implement a method to perform basic string compression using the counts of repeated characters. For ...
- JAVA的String 类【转】
String类 1.String对象的初始化 由于String对象特别常用,所以在对String对象进行初始化时,Java提供了一种简化的特殊语法,格式如下: String s = “abc”; s ...
- bzoj 2251: [2010Beijing Wc]外星联络【SA】
先求SA,然后按字典序从小到大枚举子串,每到一个后缀从长到短枚举子串(跳过长为he[i]的和前一段重复的子串),然后维护一个点p,保证i~p之间最小的he>=当前枚举长度,p是单调向右移的 然后 ...
- poj 2774 Long Long Message【SA】
把两个串接到一起求一个SA,然后找最大的sa[i]和sa[i-1]不是一个串的he[i] #include<iostream> #include<cstdio> #includ ...
随机推荐
- 攻防世界_MISC进阶区_Get-the-key.txt(详细)
攻防世界MISC进阶之Get-the-key.txt 啥话也不说,咱们直接看题吧! 首先下载附件看到一个压缩包: 我们直接解压,看到一个文件,也没有后缀名,先用 file 看一下文件属性: 发现是是L ...
- Sentry(v20.12.1) K8S 云原生架构探索,SENTRY FOR JAVASCRIPT Source Maps 详解
系列 Sentry-Go SDK 中文实践指南 一起来刷 Sentry For Go 官方文档之 Enriching Events Snuba:Sentry 新的搜索基础设施(基于 ClickHous ...
- Redis 设计与实现 10:五大数据类型之有序集合
有序集合 sorted set (下面我们叫zset 吧) 有两种编码方式:压缩列表 ziplist 和跳表 skiplist. 编码一:ziplist zset 在 ziplist 中,成员(mem ...
- linux线程数限制与zabbix监控
Linux最大线程数限制及当前线程数查询 最大线程数计算方式: n = total_memory/128k; Linux用户线程数限制而导致的程序异常为 java.lang.OutOfMemoryEr ...
- oracle动态采样导致数据库出现大量cursor pin s wait on x等待
生产库中,突然出现了大量的cursor pin s wait on x等待,第一反应是数据库出现了硬解析,查看最近的DDL语句,没有发现DDL.那么有可能这个sql是第一次进入 在OLTP高并发下产生 ...
- 【Android】报错 Please ensure Hyper-V is disabled in Windows Features, or refer to the Intel HAXM 的解决方案
参考文章 实测华为锐龙本(adm yes)安装Android avd虚拟机教程 环境 Android Studio 3.6; Windows 1909; AMD Ryzen 4800U with Ra ...
- SourceGenerator入门指北
SourceGenerator介绍 SourceGenerator于2020年4月29日在微软的.net blog首次介绍,大概说的是开发者编可以写分析器,在项目代码编译时,分析器分析项目既有的静态代 ...
- innodb锁和事物
• InnoDB存储引擎支持行级锁,其大类可以细分为共享锁和排它锁两类• 共享锁(S):允许拥有共享锁的事务读取该行数据.当一个事务拥有一行的共享锁时,另外的事务可以在同一行数据也获得共享锁,但另外的 ...
- uni-app开发经验分享十七: 开发微信公众号(H5)JSSDK 的使用方式
因为这个jssdk被uni-app坑了好多天,作者说支持1.4版本,但是我用1.4的两个分享的新方法一直不支持. 最后只能放弃了,期待什么时候能更新上. 基本的使用方法:第一步 - 下载使用方式下载地 ...
- Py变量,递归,作用域,匿名函数
局部变量与全局变量 全局变量:全局生效的变量,在顶头的,无缩进的定义的变量. 局部变量:函数内生效的变量,在函数内定义的变量. name='1fh' def changename(): name='s ...