大意: 求重复$k$次的子串个数

枚举重复长度$i$, 把整个串分为$n/i$块, 如果每块可以$O(1)$计算, 那么最终复杂度就为$O(nlogn)$

有个结论是: 以$j$开头的子串重复次数最大为$1+\lfloor\frac{lcp(j,j+i)}{i}\rfloor$

先特判掉$k=1$的情况, 然后枚举每个块开头的位置, 计算出$lcp$的值$p$, 由于$k>1$, 合法位置的$lcp$值至少要跨越一个块, 可以得到

  • 若$p\ge ki-1$, 那么这个块内所有点都合法
  • 若$k(i-1)\le p< ki-1$, 那么这个块内只有前面一部分合法, 后面一部分一定不合法
  • 若$p<k(i-1)$, 那么前面一部分一定不合法, 可能合法的点只有该块末尾与下一块的最长公共后缀的长度
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head const int N = 1e6+10;
int c[N],rk[N],rk2[N],h[N],sa[N]; const int Max_N = N;
namespace SA_IS {
int *sa; template <typename _Char>
void push_S(const _Char s[],const int x,int cur[]) { sa[--cur[static_cast<int>(s[x])]] = x; };
template <typename _Char>
void push_L(const _Char s[],const int x,int cur[]) { sa[cur[static_cast<int>(s[x])]++] = x; };
template <typename _Char>
void induced_sort(const int v[], const int n, const int m, const _Char s[], char type[], int cnt[], int n1) {
std::fill(sa, sa + n, 0);
int *cur = cnt + m;
std::copy(cnt, cnt + m, cur);
for (int i = n1 - 1; i >= 0; --i) push_S(s,v[i],cur);
std::copy(cnt, cnt + m - 1, cur + 1);
for (int i = 0; i < n; ++i)
if (sa[i] > 0 && type[sa[i] - 1] == 0)
push_L(s,sa[i] - 1,cur);
std::copy(cnt, cnt + m, cur);
for (int i = n - 1; i >= 0; --i)
if (sa[i] > 0 && type[sa[i] - 1])
push_S(s,sa[i] - 1,cur);
}
template <typename _Char>
bool lms_equal(const _Char s[],char type[],int x, int y) {
if (s[x] == s[y])
while (s[++x] == s[++y] && type[x] == type[y])
if (type[x] == 2 || type[y] == 2)
return true;
return false;
} char *tp;
bool cmp(const int x) {
return tp[x]!=2;
}
template <typename _Char>
void sais_core(const int n, const int m, const _Char s[], char type[], int lms[], int cnt[]) {
int n1 = 0, ch = -1;
tp = type;
type[n - 1] = 1;
for (int i = n - 2; i >= 0; --i) type[i] = s[i] == s[i + 1] ? type[i + 1] : s[i] < s[i + 1]; std::fill(cnt, cnt + m, 0);
for (int i = 0; i < n; ++i) ++cnt[static_cast<int>(s[i])];
std::partial_sum(cnt, cnt + m, cnt); for (int i = 1; i < n; ++i)
if (type[i - 1] == 0 && type[i] == 1)
type[i] = 2, lms[n1++] = i;
induced_sort(lms,n,m,s,type,cnt,n1); int *s1 = std::remove_if(sa, sa + n, cmp);
for (int i = 0; i < n1; ++i) s1[sa[i] >> 1] = ch += ch <= 0 || !lms_equal(s,type,sa[i], sa[i - 1]);
for (int i = 0; i < n1; ++i) s1[i] = s1[lms[i] >> 1]; if (ch + 1 < n1)
sais_core(n1, ch + 1, s1, type + n, lms + n1, cnt + m);
else
for (int i = 0; i < n1; ++i) sa[s1[i]] = i; for (int i = 0; i < n1; ++i) lms[n1 + i] = lms[sa[i]];
induced_sort(lms + n1,n,m,s,type,cnt,n1);
}
template <typename _Char>
void main(const _Char s[], const int n, const int m) {
static int _lms[Max_N], _cnt[Max_N << 1];
static char _type[Max_N << 1];
sais_core(n + 1, m, s, _type, _lms, _cnt);
}
} void calc(int *s,int *rk,int n) {
REP(i,1,n) ++sa[i];
for(int i=1;i<=n;i++) rk[sa[i]]=i;
for(int i=1,j,k=0;i<=n;i++) {
if(k) k--;
j=sa[rk[i]-1];
while (s[i+k]==s[j+k]) k++;
h[rk[i]] = k;
}
} void build(int *a,int *rk,int n) {
SA_IS::sa = sa;
a[n+1] = 0;
SA_IS::main(a+1,n,128);
calc(a,rk,n);
} int Log[N],f[20][N],g[20][N];
void init(int a[N],int f[20][N],int n) {
Log[0] = -1;
REP(i,1,n) f[0][i] = a[i], Log[i]=Log[i>>1]+1;
REP(j,1,19) for (int i=0;i+(1<<j-1)-1<=n; ++i) {
f[j][i] = min(f[j-1][i],f[j-1][i+(1<<j-1)]);
}
}
int RMQ(int f[20][N], int l, int r) {
int t = Log[r-l+1];
return min(f[t][l],f[t][r-(1<<t)+1]);
}
int n, k, a[N];
char s[N];
int lcp(int x, int y) {
x = rk[x], y = rk[y];
if (x>y) swap(x,y);
return RMQ(f,x+1,y);
}
int lcs(int x, int y) {
x = rk2[n-x+1], y = rk2[n-y+1];
if (x>y) swap(x,y);
return RMQ(g,x+1,y);
} int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%s", &k, s+1);
n = strlen(s+1);
if (k==1) {
printf("%lld\n",(ll)n*(n+1)/2);
continue;
}
REP(i,1,n) a[i]=s[i]-'a'+1;
build(a,rk,n);
init(h,f,n);
reverse(a+1,a+1+n);
build(a,rk2,n);
init(h,g,n);
ll ans = 0;
REP(i,1,n) for (int j=1; j+i<=n; j+=i) {
int p = lcp(j,j+i);
if (p>=k*i) ans += i;
else if (p>=k*i-i) ans += p-k*i+i+1;
else {
if (j+2*i-1>n) break;
int s = lcs(j+i-1,j+2*i-1);
if (!s) continue;
s = min(s, i-1);
int p2 = lcp(j+i-s,j+2*i-s);
if (p2>=k*i-i) ans += min(p2-k*i+i+1,s);
}
}
printf("%lld\n", ans);
}
}

hdu 6661 Acesrc and String Theory (后缀数组)的更多相关文章

  1. HDU6661 Acesrc and String Theory【SA】

    Acesrc and String Theory Problem Description Acesrc is a famous string theorist at Nanjing Universit ...

  2. HDU 6194 string string string(后缀数组+RMQ)

    string string string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  3. 【HDU 5030】Rabbit's String (二分+后缀数组)

    Rabbit's String Problem Description Long long ago, there lived a lot of rabbits in the forest. One d ...

  4. HDU 1403 Longest Common Substring(后缀数组,最长公共子串)

    hdu题目 poj题目 参考了 罗穗骞的论文<后缀数组——处理字符串的有力工具> 题意:求两个序列的最长公共子串 思路:后缀数组经典题目之一(模版题) //后缀数组sa:将s的n个后缀从小 ...

  5. HDU - 6223 Infinite Fraction Path (倍增+后缀数组)

    题意:给定一个长度为n(n<=150000)的字符串,每个下标i与(i*i+1)%n连边,求从任意下标出发走n步能走出的字典序最大的字符串. 把下标看成结点,由于每个结点有唯一的后继,因此形成的 ...

  6. HDU - 5008 Boring String Problem (后缀数组+二分法+RMQ)

    Problem Description In this problem, you are given a string s and q queries. For each query, you sho ...

  7. HDU 5008 Boring String Problem(后缀数组+二分)

    题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring ...

  8. HDU 3336 Count the string(next数组运用)

    Count the string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  9. HDU:3336-Count the string(next数组理解)

    Count the string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Pr ...

随机推荐

  1. CESIUM内置shader变量和函数[转]

    cesium中内置了一些常量.变量和函数,在vs和fs中可直接使用. 内置uniform 内置uniform主要置于AutomaticUniforms类里面,该类私有未开放文档. czm_backgr ...

  2. CNS、ENS和PNS的发育过程

    central nervous system (CNS) peripheral nervous system (PNS) enteric nervous system (ENS) 做这部分的科研必须要 ...

  3. WebSocket——SuperWebSocket实现服务端和客户端

    WebSocket——SuperWebSocket实现服务端和客户端具体实现如下: 注:本作者是基于vs2019 enterprise版本,所有项目均为.Net Framwork4.7版本(因为Web ...

  4. MySQL两地三中心方案初步设计【转】

    整体内容会按照如下的方式来进行设计: 首先说下方案的背景,我参考了一些资料(参见附件). 方案背景 随着互联网业务快速发展,多IDC的业务支撑能力和要求也逐步提升,行业内的“两地三中心”方案较为流行. ...

  5. Python3基础 str : 对字符串进行切片

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  6. 【插件式框架探索系列】应用程序域(AppDomain)

    应用程序域(AppDomain)已经不是一个新名词了,只要熟悉.net的都知道它的存在,不过我们还是先一起来重新认识下应用程序域吧,究竟它是何方神圣. 应用程序域 众所周知,进程是代码执行和资源分配的 ...

  7. Spring cloud微服务安全实战-4-2微服务安全的新挑战

    微服务的环境下,我的业务逻辑不再是在一个单一的进程里,而是分散了很多的进程里.订单.物流.库存.价格.每一个tomcat都是一个进程. 每一个进程,每一个tomcat都有自己的入口点.那么就导致我防范 ...

  8. getField和getDeclaredField的区别

    这两个方法都是用于获取字段getField 只能获取public的,包括从父类继承来的字段.getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段. ...

  9. python学习:python打包成exe

    1) 安装pyinstaller pip install pyinstaller 目前已经支持 python 3.7 版本 2) 打开cmd窗口,进入到要打包的python文件所在目录, pyinst ...

  10. locate home of running java application

    1. find the target process id of your java app jps [-lm] in my case: [lenmom@Mi- bin]$ jps -l sun.to ...