联考T2:「串串串」

11490DX Re: Master Lv.15

虽然这是我的第一篇正式博客…

但是首先:

神金题目名!!!

题目大意:

给定一个小写字母字符串,要求在这个字符串中找到一个后缀,可以给这个后缀每一个字符映射另外一个小写字母(可以为其本身)。但是字母一样映射的那个字母就必须一样。

求出字典序最大的后缀映射。

SAMPLE I/O

I:

1
2
3
2
abcba
qwertyuiop

O:

1
2
zyzx
zyxwvutsrq

测试点限制:

时间限制:

空间限制:

subtask idx 特殊性质 分数
/
/
字符集为{}
字符集为{}
字符集为{}
/

思路:

我们可以知道,要使这个映射他的字典序最大,那么第一位必须是,后面就跟着,,…我们可以想到最暴力的做法:

枚举每一个后缀,将这个后缀的第一种字母赋为,第二种字母赋为,一直到最后一个字母,期望得分

考虑到后面的字符集为{},可以考虑不是可以映射为就是可以映射为,于是我们就可以找到一个最长的连续的子段,如果有相同的最长的就比较这个字段后面连续的子段的长度,更短的肯定更优,然后第三段连续子段也是更长更优…于是我们就可以找这两个字符串的最长公共前缀来比较,期望得分分。

我们可以沿用之前最长公共前缀的思路,把整个字符串拆成26个0/1字符串,1表示这个位置存在该字母,0表示不存在。从右往左搜索这个字母从左往右第一次出现的位置,之后把出现位置最早的字母赋值为,第二早的赋值为,…之后我们就可以使用二分+哈希求得两个后缀答案的最长公共前缀,然后再看第一次出现差异的地方的字符即可得到顺序。可以通过这些0/1字符串转哈希,来二分差异位置。

复杂度为(其中)。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+4, A = 29;
typedef unsigned long long ull;
char s[N];
int ord[A], rk[N][A];//ord:从第i个字符往后,26个字母的出现顺序. RK:26个字母的排名
int lst[A];//这个字母从左往右第一次出现的位置
int n, t;
ull has[N][A], mi[N], base = 107;//has:hash
bool cmp(int x, int y){
return lst[x] < lst[y];
}
ull gethash(int l, int r, int let){//获取从l~r的关于某一个特定字母let的哈希值(见下文)
return has[r][let] - has[l-1][let] * mi[r-l+1];
}
void init(){
mi[0] = 1;
for(int i=1;i<N;i++){
mi[i] = (mi[i-1]*base);//其实是mod2^63......
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin>>t;
init();
while(t--){
cin>>(s+1);
n=strlen(s+1);
for(int i=0;i<26;i++){
ord[i] = i, lst[i] = n+1;
}
for(int i=n;i>=1;i--){
lst[s[i]-'a'] = i;
sort(ord, ord+26, cmp);
for(int j=0;j<26;j++){
rk[i][ord[j]] = 25-j;//求rk数组,把第一名换为z,第二名换为y...等等
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<26;j++){//这个是把26个0/1字符串转化为哈希值,求最长公共前缀。
//如果这两个前缀的哈希值相等,那么就是公共前缀。否则就不是公共前缀
//因为最长公共前缀满足在他前面的是公共前缀,在他后面的不是公共前缀
//所以可以用二分来求得两个后缀答案的最长公共前缀!
has[i][j] = has[i-1][j]*base;
}
has[i][s[i]-'a'] ++;
}
int maxid = n;
for(int i=n-1;i>=1;i--){
int l=1, r=n-maxid+1, mid;//枚举最长公共前缀长度
while(l<=r){
ull x1, x2;
x1=x2=0;//预想子串和答案子串分别的哈希值
mid=l+r>>1;
for(int j=0;j<26;j++){
x1 += gethash(i, i+mid-1, j)*rk[i][j];
x2 += gethash(maxid, maxid+mid-1, j)*rk[maxid][j];
}
if(x1==x2) l = mid+1;
else r = mid-1;
}
if(maxid+r>n || rk[i][s[i+r]-'a'] > rk[maxid][s[maxid+r]-'a']) maxid = i;
}
for(int i=maxid;i<=n;i++){
cout<<char(rk[maxid][s[i]-'a']+'a');
}
cout<<"\n";
}
return 0;
}

OVER.

  • Title: 联考T2:「串串串」
  • Author: 11490DX
  • Created at : 2024-09-13 21:08:11
  • Updated at : 2025-02-14 13:07:13
  • Link: https://11490dx.net/2024/09/13/20240913-T2/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments