1. 程式人生 > 實用技巧 >Luogu5826 【模板】子序列自動機

Luogu5826 【模板】子序列自動機

子序列自動機

首先考慮\(O(nm)\)的暴力

\(nxt[i][j]\)表示\(i\)之後\(j\)出現的最前位置,很容易處理(具體看程式碼)

匹配子串一個個跳過去就好了(同上,具體看程式碼)

C++ Code:

#include<bits/stdc++.h>
using namespace std;
int opt,n,q,m,g,w,k;
int nxt[100005][105],a[100005];
bool flag;
int main() 
{
	scanf("%d%d%d%d",&opt,&n,&q,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1;i<=m;i++)
		nxt[n][i]=-1;
	for (int i=n;i;i--)
	{
		for (int j=1;j<=m;j++)
			nxt[i-1][j]=nxt[i][j];
		nxt[i-1][a[i]]=i;
	}
	while (q --> 0)
	{
		scanf("%d",&w);
		g=0;
		flag=false;
		for (int i=1;i<=w;i++)
		{
			scanf("%d",&k);
			if (!(~nxt[g][k]))
				flag=true; else
				g=nxt[g][k];
		}
		puts(flag?"No":"Yes");
	}
	return 0;
}

考慮優化,我們求\(nxt\),可以用兩種方法處理

\(1.\)因為每次只改一個值,用主席樹維護

\(2.\)二分\(nxt\)的位置,將每個數的位置丟進該數的\(vector\)中,\(vector\)原本就是有序的,下面的程式碼是二分版的

C++ Code:

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int opt,n,q,m,g,st,k,a[N];
vector<int>v[N];
bool flag;
int find(int u,int c)
{
	vector<int>::iterator pos=upper_bound(v[u].begin(),v[u].end(),c);
	if (pos==v[u].end())
		return -1; else
		return *pos;
}
int main() 
{
	scanf("%d%d%d%d",&opt,&n,&q,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),v[a[i]].push_back(i);
	for (;q --> 0;)
	{
		scanf("%d",&g);
		flag=false;
		st=0;
		for (;g --> 0;)
		{
			scanf("%d",&k);
			if (flag)
				continue;
			st=find(k,st);
			if (!(~st))
				flag=true;
		}
		puts(flag?"No":"Yes");
	}
	return 0;
}