Description
SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999。
他给出了一个字符串T,字符串T中有且仅有4种字符 ‘A’, ‘B’, ‘C’, ‘D’。现在他要求蒟蒻yts1999构造一个新的字符串S,构造的方法是:进行多次操作,每一次操作选择T的一个子串,将其加入S的末尾。
对于一个可构造出的字符串S,可能有多种构造方案,Oxer定义构造字符串S所需的操作次数为所有构造方案中操作次数的最小值。
Oxer想知道对于给定的正整数N和字符串T,他所能构造出的所有长度为N的字符串S中,构造所需的操作次数最大的字符串的操作次数。
蒟蒻yts1999当然不会做了,于是向你求助。
Sample Input
5
ABCCAD
Sample Output
5
首先这题可以确定是矩阵乘法。
考虑构建矩阵,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为
i
i
i字符最少往后随便加
f
[
i
]
[
j
]
−
1
f[i][j]-1
f[i][j]−1个字符后再加一个字符
j
j
j可以是这个字符串不在原串中。
如果构造出这个矩阵可以考虑二分+矩阵乘法验证。
那么就是如何构造出这个矩阵的问题。
考虑设
h
h
[
i
]
[
j
]
hh[i][j]
hh[i][j]为SAM编号为i的点最少往后随便加
h
h
[
i
]
[
j
]
−
1
hh[i][j]-1
hh[i][j]−1个字符后再加一个字符
j
j
j可以是这个字符串不在原串中。
那么
h
h
[
i
]
[
j
]
=
m
i
n
(
h
h
[
i
]
[
j
]
,
h
h
[
t
[
i
]
.
s
o
n
[
c
]
]
[
j
]
)
hh[i][j]=min(hh[i][j],hh[t[i].son[c]][j])
hh[i][j]=min(hh[i][j],hh[t[i].son[c]][j])
f
[
i
]
[
j
]
=
h
h
[
t
[
1
]
.
s
o
n
[
i
]
]
[
j
]
f[i][j]=hh[t[1].son[i]][j]
f[i][j]=hh[t[1].son[i]][j]
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL _min(LL x, LL y) {return x < y ? x : y;}
LL _max(LL x, LL y) {return x > y ? x : y;}
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
struct node {
LL a[4][4];
node() {memset(a, -1, sizeof(a));}
friend node operator * (node a, node b) {
node c;
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
for(int k = 0; k < 4; k++)
c.a[i][j] = (c.a[i][j] == -1 ? a.a[i][k] + b.a[k][j] : _min(c.a[i][j], a.a[i][k] + b.a[k][j]));
return c;
}
} ST, ONE;
struct SAM {
int son[4], len, fa;
} t[1000010]; int root, last, cnt;
int hh[1000010][4];
char ss[1000010];
bool v[1000010];
int n; LL len;
void insam(int c) {
int np = ++cnt, p = last;
t[np].len = t[p].len + 1;
while(p && !t[p].son[c]) t[p].son[c] = np, p = t[p].fa;
if(!p) t[np].fa = root;
else {
int q = t[p].son[c];
if(t[p].len + 1 == t[q].len) t[np].fa = q;
else {
int nq = ++cnt;
t[nq] = t[q];
t[nq].len = t[p].len + 1;
t[q].fa = t[np].fa = nq;
while(p && t[p].son[c] == q) t[p].son[c] = nq, p = t[p].fa;
}
} last = np;
}
void bt_SAM() {
cnt = last = root = 1;
for(int i = 1; i <= n; i++) insam(ss[i] - 'A');
}
void dfs(int x) {
if(v[x]) return ;
v[x] = 1;
for(int i = 0; i < 4; i++) if(t[x].son[i]){
dfs(t[x].son[i]), hh[x][i] = 999999999;
} for(int i = 0; i < 4; i++) {
if(!t[x].son[i]) {hh[x][i] = 1; continue;}
for(int j = 0; j < 4; j++) hh[x][j] = _min(hh[x][j], hh[t[x].son[i]][j] + 1);
}
}
void get() {
dfs(root);
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(t[1].son[i]) ST.a[i][j] = hh[t[1].son[i]][j];
else ST.a[i][j] = 999999999;
}
bool check(LL mid) {
node ans = ONE, A = ST;
while(mid) {
if(mid & 1LL) ans = ans * A;
A = A * A; mid /= 2LL;
} LL mn = ans.a[0][0];
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
mn = _min(mn, ans.a[i][j]);
return mn >= len;
}
int main() {
scanf("%lld", &len);
scanf("%s", ss + 1);
n = strlen(ss + 1);
bt_SAM();
get();
LL l = 1, r = len, ans = len + 1;
for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) ONE.a[i][j] = 0;
for(int i = 0; i < 4; i++) ONE.a[i][i] = 1;
while(l <= r) {
LL mid = (l + r) / 2;
if(check(mid)) r = mid - 1, ans = mid;
else l = mid + 1;
} printf("%lld\n", ans);
return 0;
}