【洛谷5826】【模板】子序列自动机

点此看题面

  • 给定一个长度为(n)的文本串,字符集大小为(m)
  • (q)次询问,每次给定一个字符串,询问是否为文本串的一个子序列。
  • (n,m,qle10^5)

思想核心:贪心

其实就这题而论,子序列自动机是一个非常简单易懂的算法,闲得无聊来水一水。

核心思想是贪心,就是考虑我们每次找到尽可能近的字符匹配一定是最优的。

方法一:主席树

由于常见字符串问题的字符集大小是(26),所以相信大家都写过一个东西,就是记录(S_{i,c})表示第(i)位之后第一个字符(c)所在位置。

要维护这个只需从后往前枚举,每次(S_i)继承(S_{i+1}),并更新(S_{i,a_{i+1}}=i+1)

这题字符集很大,考虑继承操作就相当于是一个版本的赋值,随后我们进行的是在新版本上的一个单点修改。

发现这就是一个用主席树实现可持久化数组的过程,非常板子。

方法二:(vector)+二分

直接对于每个字符集开一个(vector),要找到第(i)位之后第一个字符(c),只要在字符(c)对应的(vector)中求出(upper\_bound(i))即可。

而且把(vector)换成(set)甚至能实现带修,这是主席树做不到的。所以说主席树直接被全方位吊打。

代码:(O(nlogn))

当然是方法二啦,不会真的有人会去写主席树吧。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,m;vector<int> S[N+5];vector<int>::iterator it;
int main()
{
	RI Qt,i,x,y;for(scanf("%d%d%d%d",&x,&n,&Qt,&m),i=1;i<=n;++i) scanf("%d",&x),S[x].emplace_back(i);//对每种字符维护个vector
	RI t;W(Qt--) {for(scanf("%d",&t),x=0;t;--t) scanf("%d",&y),
		~x&&(x=(it=upper_bound(S[y].begin(),S[y].end(),x))==S[y].end()?-1:*it);puts(~x?"Yes":"No");}//利用upper_bound查找下一位
	return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/SubSeqAutomation.html