用户工具

站点工具


2020-2021:teams:legal_string:王智彪:序列自动机

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
2020-2021:teams:legal_string:王智彪:序列自动机 [2021/08/01 01:13]
王智彪 创建
2020-2021:teams:legal_string:王智彪:序列自动机 [2021/08/01 19:25] (当前版本)
王智彪
行 16: 行 16:
  
 构建复杂度是 $O(n|\sum|)$ 。 构建复杂度是 $O(n|\sum|)$ 。
 +
 +===== 算法实现 =====
 +
 +==== 构造 ====
 +
 +<hidden 构造>
 +
 +<code cpp>
 +
 +struct SQAM {
 + int ch[MAXL][MAXM],​las[MAXM];​
 + int root;
 + void init() {
 + root=1;//​构造根节点 ​
 + }
 + void work(char s[]) {
 + int len=strlen(s);​
 + for(int i=len-1;​i>​=0;​i--) {
 + for(int j=0;​j<​MAXM;​j++) ch[i+2][j]=las[j];//​每个字符是一个节点 根节点是1 所以0位置字符状态为2 ​
 + las[s[i]-'​a'​]=i+2;​
 + }
 + for(int i=0;​i<​MAXM;​i++) ch[root][i]=las[i];​
 + }
 +}s1,s2;
 +
 +</​code>​
 +
 +</​hidden>​
 +
 +===== 代码练习 =====
  
 问题导入: 问题导入:
行 68: 行 98:
 如果要支持带修,把 $vector$ 换成平衡树即可(修改变成 $O(logn)$ 的)。 如果要支持带修,把 $vector$ 换成平衡树即可(修改变成 $O(logn)$ 的)。
  
 +<hidden 带修代码>​
  
 +<code cpp>
 +
 +#​include<​iostream>​
 +#​include<​cstdlib>​
 +using namespace std;
 +const int N=1e5+10;
 +struct fhq_treap{
 + int lson,rson;
 + int val,key;
 + int size;
 +};
 +fhq_treap fhq[N];
 +int root[N],​a[N];​
 +int tot;
 +inline int newnode(int val){
 + tot++;
 + fhq[tot].key=rand();​
 + fhq[tot].val=val;​
 + fhq[tot].size=1;​
 + return tot;
 +}
 +inline void pushup(int pos){
 + fhq[pos].size=fhq[fhq[pos].lson].size+fhq[fhq[pos].rson].size+1;​
 +}
 +void split(int pos,int val,int &x,int &y){
 + if(!pos){
 + x=y=0;
 + return ;
 + }
 + if(fhq[pos].val<​=val){
 + x=pos;
 + split(fhq[pos].rson,​val,​fhq[x].rson,​y);​
 + }
 + else{
 + y=pos;
 + split(fhq[pos].lson,​val,​x,​fhq[y].lson);​
 + }
 + pushup(pos);​
 +}
 +int merge(int x,int y){
 + if(!x||!y)
 + return x+y;
 + if(fhq[x].key<​fhq[y].key){
 + fhq[x].rson=merge(fhq[x].rson,​y);​
 + pushup(x);​
 + return x;
 + }
 + else{
 + fhq[y].lson=merge(x,​fhq[y].lson);​
 + pushup(y);​
 + return y;
 + }
 +}
 +void ins(int &​pos,​int val){
 + int x,y;
 + split(pos,​val,​x,​y);​
 + pos=merge(merge(x,​newnode(val)),​y);​
 +}
 +void del(int &​pos,​int val){
 + int x,y,z;
 + split(pos,​val-1,​x,​y);​
 + split(y,​val,​y,​z);​
 + y=merge(fhq[y].lson,​fhq[y].rson);​
 + pos=merge(merge(x,​y),​z);​
 +}
 +int get_suc(int &​pos,​int val){
 + int x,y;
 + split(pos,​val,​x,​y);​
 + int ans=y;
 + while(fhq[ans].lson)
 + ans=fhq[ans].lson;​
 + pos=merge(x,​y);​
 + return fhq[ans].val;​
 +}
 +int main(){
 + int t,n,m,k;
 + cin>>​t>>​n>>​m>>​k;​
 + for(int i=1;​i<​=n;​i++){
 + cin>>​a[i];​
 + root[a[i]]=merge(root[a[i]],​newnode(i));​
 + }
 + int opt;
 + int x,y;
 + int pos=0;
 + bool flag;
 + for(int i=1;​i<​=m;​i++){
 + cin>>​opt;​
 + if(opt){
 + cin>>​x;​
 + pos=0;
 + flag=true;​
 + for(int j=1;​j<​=x;​j++){
 + cin>>​y;​
 + pos=get_suc(root[y],​pos);​
 + if(!pos)
 + flag=false;​
 + }
 + if(flag)
 + cout<<"​Yes\n";​
 + else
 + cout<<"​No\n";​
 + }
 + else{
 + cin>>​x>>​y;​
 + del(root[a[x]],​x);//​删除
 + a[x]=y;//​修改
 + ins(root[a[x]],​x);//​插入
 + }
 + }
 +}
 +
 +</​code>​
 +
 +</​hidden>​
 +
 +1.[[https://​www.luogu.com.cn/​problem/​P4112#​submit]]
 +
 +==== 题意 ====
 +
 +给定两个长度不超过 $2000$ 的字符串,分别设为 $s_{1},​s_{2}$ ​
 +
 +分为四个问题:
 +
 +  - 求 $s_{1}$ 的最短的子串且不是 $s_{2}$ 的子串
 +  - 求 $s_{1}$ 的最短的子串且不是 $s_{2}$ 的子序列
 +  - 求 $s_{1}$ 的最短的子序列且不是 $s_{2}$ 的子串
 +  - 求 $s_{1}$ 的最短的子序列且不是 $s_{2}$ 的子序列
 +
 +如果没找到输出 $-1$ 。
 +
 +==== 题解 ====
 +
 +显然要分别对两个串建立后缀自动机和序列自动机
 +
 +因为要找最短的,所以 $BFS$ 开始搜,不管是什么自动机,从根节点往下开始搜,搜到 $s_{1}$ 有结点且 $s_{2}$ 没有的结点就可以了,如果到最后都没找到输出 $-1$ 即可。
 +
 +<hidden 代码>
 +
 +<code cpp>
 +
 +#​include<​bits/​stdc++.h>​
 +using namespace std;
 +typedef long long ll;
 +
 +const int MAXL=2021;
 +const int MAXM=46;
 +
 +const int MAXN=2021;
 +struct SAM {
 + int las,tot;
 + struct NODE {
 + int ch[MAXM];
 + int len,fa;
 + NODE() {
 + memset(ch,​0,​sizeof(ch));​
 + len=0;
 + fa=0;
 + }
 + } dian[MAXN<<​1];​
 + void init() {
 + las=tot=1;​
 + }
 + void add(int c) {
 + int p=las;
 + int np=las=++tot;​
 + dian[np].len=dian[p].len+1;​
 + for(; p&&​!dian[p].ch[c];​ p=dian[p].fa)dian[p].ch[c]=np;​
 + if(!p)dian[np].fa=1;//​以上为case 1
 + else {
 + int q=dian[p].ch[c];​
 + if(dian[q].len==dian[p].len+1)dian[np].fa=q;//​以上为case 2
 + else {
 + int nq=++tot;
 + dian[nq]=dian[q];​
 + dian[nq].len=dian[p].len+1;​
 + dian[q].fa=dian[np].fa=nq;​
 + for(; p&&​dian[p].ch[c]==q;​ p=dian[p].fa)dian[p].ch[c]=nq;​ //​以上为case 3
 + }
 + }
 + }
 +}S1,S2;
 +
 +char A[MAXL],​B[MAXL];​
 +int la,lb;
 +
 +struct SQAM {
 + int ch[MAXL][MAXM],​las[MAXM];​
 + int root;
 + void init() {
 + root=1;//​构造根节点 ​
 + }
 + void work(char s[]) {
 + int len=strlen(s);​
 + for(int i=len-1;​i>​=0;​i--) {
 + for(int j=0;​j<​MAXM;​j++) ch[i+2][j]=las[j];//​每个字符是一个节点 根节点是1 所以0位置字符状态为2 ​
 + las[s[i]-'​a'​]=i+2;​
 + }
 + for(int i=0;​i<​MAXM;​i++) ch[root][i]=las[i];​
 + }
 +}s1,s2;
 +
 +struct Node {
 + int a,b,s;
 +};
 +int vis[MAXL<<​1][MAXL<<​1];​
 +
 +int BFS(int v){
 + queue<​Node>​q;​
 + vis[1][1]=v;​
 + Node ntmp;
 + ntmp.a=1,​ntmp.b=1,​ntmp.s=0;​
 + q.push(ntmp);​
 + while(!q.empty()) {
 + ntmp=q.front();​
 + int a=ntmp.a,​b=ntmp.b,​s=ntmp.s;​
 + //​printf("​%d %d %d %d\n",​a,​b,​s,​v);​
 + q.pop();
 + for(int i=0,​da,​db;​i<​MAXM;​i++) {
 + if(v==1) {
 + da=S1.dian[a].ch[i],​db=S2.dian[b].ch[i];​
 + }else if(v==2) {
 + da=S1.dian[a].ch[i],​db=s2.ch[b][i];​
 + }else if(v==3) {
 + da=s1.ch[a][i],​db=S2.dian[b].ch[i];​
 + }else if(v==4) {
 + da=s1.ch[a][i],​db=s2.ch[b][i];​
 + }
 + //​printf("​%d %d %d\n",​da,​db,​v);​
 + if(vis[da][db]==v) continue;
 + if(da&&​!db) return s+1;
 + if(da&&​db) {
 + Node nntmp;
 + nntmp.a=da,​nntmp.b=db,​nntmp.s=s+1;​
 + q.push(nntmp);​
 + vis[da][db]=v;​
 + }
 + }
 + }
 + return -1;
 +}
 +
 +int main() {
 + scanf("​%s%s",​A,​B);​
 + la=strlen(A);​ lb=strlen(B);​
 + s1.init();​s2.init();​S1.init();​S2.init();​
 + for(int i=0; i<la; i++) {
 + S1.add(A[i]-'​a'​);​
 + }
 + s1.work(A);​
 + for(int i=0; i<lb; i++) {
 + S2.add(B[i]-'​a'​);​
 + }
 + s2.work(B);​
 + int a1=BFS(1),​a2=BFS(2),​a3=BFS(3),​a4=BFS(4);​
 + printf("​%d\n%d\n%d\n%d\n",​a1,​a2,​a3,​a4);​
 + return 0;
 +}
 +
 +</​code>​
 +
 +</​hidden>​
2020-2021/teams/legal_string/王智彪/序列自动机.1627751604.txt.gz · 最后更改: 2021/08/01 01:13 由 王智彪