Death DBMS题解(AC自动机)
好题
观察这道题,发现有关字串的题目,一般来说,这种题都要构建\(AC\)自动机,所以考虑构建。
构建之后,原来的所有 \(fail\) 是一个树形结构。
解法 \(1\):
考虑从询问入手,那么对于每一个询问,等价于就是查询每一个 \(Q_i\) 包含的后缀的最大值,再对这些最大值取 \(max\),那么 \(Q\) 在跳 \(fail\) 树时,查询所有 \(Q_i\) 点到根的最大值,再将这些最大值取 \(max\),就是答案,考虑:树剖 \(+\) 单点修改。
时间复杂度 \(O(n~log^2~n)\)
解法 \(2\):
考虑从修改入手,那么对于修改字符串 \(s\),代表字符串 \(s\) 的点的整个子树的最大值都会受到影响,修改整个子树内的每个点的最大值,查询 \(Q\) 时,让 \(Q\) 在跳 \(fail\) 树时对于每一个 \(Q_i\) 的值取一个 \(max\),那么考虑:子树修改 \(+\) 单点查询
注意:用 \(multiset\)(可重并查集,且自动排序) 去维护
时间复杂度 \(O(n~log^2~n)\),因为有些东西并不是 \(O(1)\) 的
解法 \(3\):
Important
我们发现:对于 \(i\) 这个节点,他这个点的值会且只会影响以他为根的这棵子树,于是我们可以考虑用 \(dfs\),在进入这个点的时候加入有关这个点的一些元素,退出这个点的时候再删除。
在线是没前途的,优秀的时间复杂度一般都是离线的,考虑将问题离线下来,然后用线段树分治,表示该时间段的最大值,每新进入一个点的时候就更新一下这个线段树,这里可以用标记永久化
然后我们发现,最大值不好删除,这里又是一个 \(Trick\) : 把删除改成撤销,每次修改时记录修改位置和原来的最大值,退出这个点时把这些位置改回来。
分析一下时间复杂度:
每次修改会产生 \(\log m\) 的时间复杂度,最多有 \(n+q\) 次修改,故时间复杂度为 \(O((n+q) \log m)\)。
每次修改最多改变 \(\log m\) 个节点,最多有 \(n+q\) 次修改,撤回的时候时间复杂度为 \(O((n+q) \log m)\)
撤去常数,\(n,m,q\) 是同一个级别,所以时间复杂度就是 \(O(n \log n)\) 级别!!!
分析一下空间复杂度:
最多要记录 \((n+q) \log n\) 的修改,所以空间复杂度是 \(O(n \log n)\) 级别,不过因为 \(n\le 3e5\),空间限制是 \(500MB\),所以可以过。
这里科普一下,\(128MB\) 就可以开一个形如 \(long~long~a[1e7]\) 的数组
贴上代码:
#include<bits/stdc++.h>
#define ll long long
#define PLL pair<ll,ll>
using namespace std;
const ll N=3e5+50,zero=0;
ll n,m;
vector<ll> e[N],word[N],q[N];
vector<PLL> sz1[N];//i在不同时段的数值
char s[N];
ll ch[N][26],idx;
void insert(char str[],ll id)
{
ll p=0,len=strlen(str);
for(ll i=0;i<len;i++)
{
ll shu=str[i]-'a';
if(!ch[p][shu]) ch[p][shu]=++idx;
p=ch[p][shu];
}
word[p].push_back(id);
}
ll fail[N];
void get_fail()
{
queue<ll> q;
for(ll i=0;i<26;i++)
if(ch[0][i]) e[0].push_back(ch[0][i]),q.push(ch[0][i]);
while(!q.empty())
{
ll p=q.front();
q.pop();
for(ll i=0;i<26;i++)
{
if(ch[p][i]) fail[ch[p][i]]=ch[fail[p]][i],e[ch[fail[p]][i]].push_back(ch[p][i]),q.push(ch[p][i]);
else ch[p][i]=ch[fail[p]][i];
}
}
}
ll ans[N],tr[N*4];
void gai(ll wz,ll l,ll r,ll le,ll ri,ll x,vector<PLL> &op)
{
if(le<=l&&ri>=r)
{
op.push_back(make_pair(wz,tr[wz]));
tr[wz]=max(tr[wz],x);
return ;
}
ll mid=(l+r)/2;
if(le<=mid) gai(wz*2,l,mid,le,ri,x,op);
if(ri>mid) gai(wz*2+1,mid+1,r,le,ri,x,op);
}
ll query(ll md)
{
ll l=1,r=m,mid,p=1,re=tr[p];
while(l<r)
{
mid=(l+r)/2;
if(md<=mid) r=mid,p=p*2;
else l=mid+1,p=p*2+1;
re=max(re,tr[p]);
}
return re;
}
void dfs(ll wz)
{
vector<PLL> tzy;
ll cd=word[wz].size();
for(ll i=0;i<cd;i++)
{
ll now=word[wz][i],cd1=sz1[now].size();
sz1[now].push_back(make_pair(m+1,zero));
if(sz1[now][0].first!=1) gai(1,1,m,1,sz1[now][0].first-1,0,tzy);
for(ll j=0;j<cd1;j++) gai(1,1,m,sz1[now][j].first,sz1[now][j+1].first-1,sz1[now][j].second,tzy);
}
cd=q[wz].size();
for(ll i=0;i<cd;i++)
{
ll now=q[wz][i];
ans[now]=max(ans[now],query(now));
}
cd=e[wz].size();
for(ll i=0;i<cd;i++)
dfs(e[wz][i]);
cd=tzy.size();
for(ll i=cd-1;i>=0;i--)
tr[tzy[i].first]=tzy[i].second;
}
int main()
{
memset(tr,-1,sizeof tr);
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=n;i++)
{
scanf("%s",s);
insert(s,i);
}
get_fail();
ll op,t1,t2;
for(ll i=1;i<=m;i++)
{
ans[i]=-114514;
scanf("%lld",&op);
if(op==1)
{
scanf("%lld %lld",&t1,&t2);
sz1[t1].push_back(make_pair(i,t2));
}
else
{
scanf("%s",s);
ll len=strlen(s),p=0;
ans[i]=-1;
for(ll j=0;j<len;j++)
{
p=ch[p][s[j]-'a'];
q[p].push_back(i);
}
}
}
dfs(0);
for(ll i=1;i<=m;i++)
if(ans[i]!=-114514) printf("%lld\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号