[UOI2024] Lady's Gift 题解
记
考虑
那么如何扩展到正解?注意到正解比
枚举长度并判定循环的部分是经典调和级数时间复杂度,而我们要对于每个找不到后继的字符串都做一遍,所以总的时间复杂度为
放代码:
#include<bits/stdc++.h>
using namespace std;
const int B=6907,P=1e9+9;
class hashing{
private:
vector<int> p,h;
public:
hashing(string a){
p.resize(a.size()+1),h.resize(a.size());
for(int i=p[0]=1;i<=a.size();i++)
p[i]=1ll*p[i-1]*B%P;
for(int i=0;i<a.size();i++)
h[i]=(1ll*(i?h[i-1]:0)*B+a[i]-96)%P;
}
inline int get(int l,int r){
return (h[r]-(l?1ll*h[l-1]*p[r-l+1]%P:0)+P)%P;
}
}; // 字符串哈希模板
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n; cin>>n;
vector<string> s(n);
vector<int> v;
for(auto &i:s)cin>>i;
vector<optional<hashing> > h(n);
for(int i=0;i<n;i++){
h[i].emplace(s[i]);
if(s[i][0]=='?')v.emplace_back(i);
} // 预处理一些信息
vector<int> p(n,-1);
auto gen=[&](int x,int y){
auto t=s[x].substr(1,n*3-1);
for(int l=1;l<=n;l++){
bool f=true;
for(int j=n*3-l;j>=n&&f;j-=l)
f&=h[x]->get(j,j+l-1)==h[x]->get(n*3-l,n*3-1);
if(f){t+=s[x][n*3-l]; break;}
}
return t;
}; // 通过 s[x] 生成 s[y],即删一个字母加一个字母的过程
auto dfs=[&](auto &&self,int x)->int{
for(int i=0;i<n;i++)
if(s[i][0]!='?'&&h[x]->get(1,n*3-1)==h[i]->get(0,n*3-2))return i;
int y=v.back(); v.pop_back(); // 没找到,取一个字符串未知的
return h[y].emplace(s[y]=gen(x,y)),p[y]=self(self,y),y;
}; // 找后继
for(int i=0;i<n;i++)
if(s[i][0]!='?')p[i]=dfs(dfs,i);
for(int i=0;i<n;i++)
if(p[i]<0)s[i][0]='q',p[i]=i; // 剩下的未确定的随便填
for(int i=0;i<n;i++)
cout<<s[i][0];
cout<<endl;
for(int i:p)cout<<i+1<<' ';
cout<<endl;
return 0;
}