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】的更多相关文章

  1. Microsoft SQL Server 【Windows 身份验证】和 【sa】都无法登录的解决方案

    1.修改启动参数:打开[SQL Server 配置管理器(SQL Server Configuration Manager)]→右键[SQL Server(MSSQLSERVER)]属性→高级(Adv ...

  2. 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 ...

  3. hdu 6661 Acesrc and String Theory (后缀数组)

    大意: 求重复$k$次的子串个数 枚举重复长度$i$, 把整个串分为$n/i$块, 如果每块可以$O(1)$计算, 那么最终复杂度就为$O(nlogn)$ 有个结论是: 以$j$开头的子串重复次数最大 ...

  4. HDOJ3374 String Problem 【KMP】+【最小表示法】

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  5. C++中int与string的相互转换【转】

    一.int转string 1.c++11标准增加了全局函数std::to_string: string to_string (int val); string to_string (long val) ...

  6. 213. String Compression【easy】

    Implement a method to perform basic string compression using the counts of repeated characters. For ...

  7. JAVA的String 类【转】

    String类 1.String对象的初始化 由于String对象特别常用,所以在对String对象进行初始化时,Java提供了一种简化的特殊语法,格式如下: String s = “abc”; s ...

  8. bzoj 2251: [2010Beijing Wc]外星联络【SA】

    先求SA,然后按字典序从小到大枚举子串,每到一个后缀从长到短枚举子串(跳过长为he[i]的和前一段重复的子串),然后维护一个点p,保证i~p之间最小的he>=当前枚举长度,p是单调向右移的 然后 ...

  9. poj 2774 Long Long Message【SA】

    把两个串接到一起求一个SA,然后找最大的sa[i]和sa[i-1]不是一个串的he[i] #include<iostream> #include<cstdio> #includ ...

随机推荐

  1. springboot源码解析-管中窥豹系列之Initializer(四)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  2. Angular入门到精通系列教程(10)- 指令(Directive)

    1. 摘要 2. 组件与指令之间的关系 2.1. 指令的种类 3. Angular 中指令的用途 4. 指令举例 4.1. 指令功能 4.2. Anuglar CLI生成基本文件 4.3. Direc ...

  3. Azure Table Storage(一) : 简单介绍

    Azure Table Storage是什么: Azure Table Storage是隶属于微软Azure Storage这个大服务下的一个子服务, 这个服务在Azure上算是老字号了, 个人大概在 ...

  4. python中re模块的使用(正则表达式)

    一.什么是正则表达式? 正则表达式,又称规则表达式,通常被用来检索.替换那些符合某个模式(规则)的文本. 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合, ...

  5. dd命令的详细介绍

    1.命令简介  dd 的主要选项: 指定数字的地方若以下列字符结尾乘以相应的数字: b=512, c=1, k=1024, w=2, xm=number m if=file #输入文件名,缺省为标准输 ...

  6. kubernets之pod的标签

    一     如何查看pod 的日志 1 通过执行命令查看日志信息 kubectl logs pod_name 二    创建带有标签的pod,一个范例的pod创建yaml文件如下所示 2.1 创建带有 ...

  7. leetcode 357. 计算各个位数不同的数字个数(DFS,回溯,数学)

    题目链接 357. 计算各个位数不同的数字个数 题意: 给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10n . 示例: 输入: 2 输出: 91 解释: 答 ...

  8. 数据分析 Pandas 简介和它的的数据结构

    本文主要讲Pandas 的Series和DataFrame 的相关属性和操作 1.Series的相关属性和操作# --Series是一种类似于一维数组的对象,只能存放一维数组!由以下两部分组成:# v ...

  9. 消息队列之activeMQ

    1.activeMQ的主要功能 实现高可用.高伸缩.高性能.易用和安全的企业级面向消息服务的系统 异步消息的消费和处理 控制消息的消费顺序 可以和Spring/springBoot整合简化编码 配置集 ...

  10. 目标检测的评价指标(TP、TN、FP、FN、Precision、Recall、IoU、mIoU、AP、mAP)

    1. TP TN FP FN ​ GroundTruth 预测结果 TP(True Positives): 真的正样本 = [正样本 被正确分为 正样本] TN(True Negatives): 真的 ...