题解:P6939 [ICPC 2017 WF] Tarot Sham Boast
声明:本篇题解使用 ABC 代替 PSR。
看到这个题,认为所有串出现的期望是一样的,实际上也确实。
但是,比如说 AAA 在 AAAA 中出现两次,只产生了
发现出现的这两次有重合,于是想到可以用 KMP 算法,求 border,然后对于 border 考虑。
border 越长越容易重,越容易重贡献就越小,所以按 border 长度排序,假了。
考虑 hack:ABACABA 和 ABBCAB,前者除了前后
这个怎么做呢?
首先证明一个结论:一个串的 border 的 border 也是原串的公共前后缀,这句话可能有点难理解,举一个例子。
ABACABA 这个串,border 是 ABA,ABA 的 border 是 A,而 A 是 ABACABA 的公共前后缀,也就是它的前缀有 A 后缀也有 A。
理解了这个例子也就会证了,border 保证了前后相同,在前缀中前缀和后缀相同,那么在后缀中也是前缀和后缀相同。
故前缀的前缀和后缀的后缀相同。
知道了这个我们就可以快速求出串的所有公共前后缀。
发现这个位置是串
这个可以决定一件事,
发现这个像字典序于是直接存到 vector 里,利用 STL 偷懒。
然后就是你发现还过不了,因为 ABACABA,这个重叠
最后还有一个样例 hack 不了的细节,就是输入的字符串长度不一定相同,这个时候短的一定会排在前面,显然。
排好序输出即可,具体实现可以参考代码,个人认为实现并不是这题的难点。
#include<bits/stdc++.h>
using namespace std;
int n,m;
string s[20],ss[20];
int fail[1000010];
int main(){
cin>>n>>m;
vector<pair<vector<int> , int>> ans;
for(int p = 1;p<=m;p++){
cin>>s[p];
ss[p] = s[p];
int len = s[p].size();
s[p] = '_' + s[p];
fail[1] = 0;
for(int i = 2,j = 0;i<=len;i++){
while(j != 0 && s[p][j + 1] != s[p][i])j = fail[j];
if(s[p][j + 1] == s[p][i])j++;
fail[i] = j;
}
int now = len;
vector<int> t;
while(now != 0){
if(2 * len - now <= n){
t.push_back(now);
}
now = fail[now];
}
ans.push_back({t , p});
}
sort(ans.begin() , ans.end());
for(int i = 0;i<ans.size();i++){
cout<<ss[ans[i].second]<<"\n";
}
return 0;
}