比赛题目训练系列15 (2020牛客暑期多校训练营(第一场))
B-Suffix Array
- 定义一个B函数,计算一个字符串所有后缀的B函数,然后对这些后缀所得的 B B B 函数的值进行字典序排序。输出排名为 1 ~ N 的后缀首字母对应原字符串的下标。
- 题解给出的结论是: c [ i ] c[i] c[i] 的值是索引 i i i 后面离它最近的与 s [ i ] s[i] s[i] 同字符的距离,然后数组c的后缀数组就等价于这个B函数生成的后缀数组。
- 我们发现,按照B数组的生成方式,在已有后缀的前面加一个字母会引起后缀的变化,比如 B ( a b ) = 00 B(ab) = 00 B(ab)=00,而 B ( a a b ) = 010 B(aab) = 010 B(aab)=010 ,但是我们计算的C数组,前面加字母就不会出现这种问题。
- 这个题的C数组又变化了一下。记录的是 N - c[i] 的值,然后直接后缀数组排序,就是答案。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int C[maxn];
char str[maxn];
//rk[i] 表示下标为i开头后缀的排名
//sa[i] 表示第i小的后缀首字母对应原字符串下标。
int rk[maxn], sa[maxn], tmp[maxn];
int N, k;
bool cmp(int i, int j)
{
if(rk[i] != rk[j]) return rk[i] < rk[j];
else{
int ri = i + k <= N ? rk[i + k] : -1;
int rj = j + k <= N ? rk[j + k] : -1;
return ri < rj;
}
}
//if s is a string, please use: get_sa(char s[], int sa[])
void get_sa(int S[], int sa[])
{
for(int i = 0; i <= N; i++){
sa[i] = i;
rk[i] = i < N ? S[i] : -1;
}
for(k = 1; k <= N; k <<= 1){
sort(sa, sa + N + 1, cmp);
tmp[sa[0]] = 0;
for(int i = 1; i <= N; i++){
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
}
for(int i = 0; i <= N; i++){
rk[i] = tmp[i];
}
}
}
int main()
{
while(cin >> N){
scanf("%s", str);
int a = N, b = N;
for(int i = N - 1; i >= 0; i--){
if(str[i] == 'a'){
if(a == N) C[i] = 0;
else C[i] = (N - a + i) % N;
a = i;
}
else{
if(str[i] == 'b'){
if(b == N) C[i] = 0;
else C[i] = (N - b + i) % N;
b = i;
}
}
}
get_sa(C, sa);
for(int i = 1; i <= N; i++){
printf("%d%c", sa[i] + 1, i == N ? '\n' : ' ');
}
}
return 0;
}
F. Infinite String Comparision
- 题意:
- 标解给了一个很奇妙的性质:两个字符串 s 1 s_1 s1, s 2 s_2 s2,第一个长度为 a,第二个长度为 b,那么如果两个字符串 s 1 ∞ , s 2 ∞ s_1^\infty, s_2^\infty s1∞,s2∞,在 a + b − g c d ( a , b ) a + b - gcd(a, b) a+b−gcd(a,b) 内无法比较字典序的大小,那么他们必然是相等的。
- 提到的一个定理。Periodicity Lemma:假设一个字符串 S 有循环节 p 和 q 并且满足 p + q ≤ ∣ S ∣ + g c d ( p , q ) p + q \le |S| + gcd(p, q) p+q≤∣S∣+gcd(p,q),那么 gcd(p, q) 也是一个循环节。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
char s1[maxn], s2[maxn];
int main()
{
ios::sync_with_stdio(false);
while(cin >> s1 >> s2){
int N = strlen(s1), M = strlen(s2);
int g = __gcd(N, M);
bool flag = false;
for(int i = 0; i < N + M - g; i++){
if(s1[i % N] > s2[i % M]){
printf(">\n");
flag = true;
break;
}
else if(s1[i % N] < s2[i % M]){
printf("<\n");
flag = true;
break;
}
}
if(!flag) printf("=\n");
}
return 0;
}
不过这个题还有一个法二,就是比较 s 1 + s 2 s_1+s_2 s1+s2 与 s 2 + s 1 s_2 + s_1 s2+s1 作比较。我们举个例子,假如 l e n ( s 1 ) = 3 , l e n ( s 2 ) = 2 len(s_1) = 3, len(s_2) = 2 len(s1)=3,len(s2)=2,那么对于而且 s 1 [ 1 : 2 ] = = s 2 [ 1 : 2 ] s_1[1:2] == s_2[1:2] s1[1:2]==s2[1:2],那么我们接下来比较的就是 s 1 [ 3 ] s_1[3] s1[3] 和 s 2 [ 1 ] s_2[1] s2[1]. 因为 s 1 [ 1 ] = = s 2 [ 1 ] s_1[1] == s_2[1] s1[1]==s2[1],因此相当于比较 s 2 [ 1 ] = = s 2 [ 3 ] s_2[1] == s_2[3] s2[1]==s2[3]. 这其实就是在比较 s 1 + s 2 s_1+s_2 s1+s2 的第三个字符与 s 2 + s 1 s_2 + s_1 s2+s1 的第三个字符。
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
string a, b;
while(cin >> a >> b){
if(a + b > b + a) puts(">");
else if(a + b == b + a) puts("=");
else puts("<");
}
return 0;
}