首页 > 其他分享 >牛客14612 string AC自动机 + 树状数组

牛客14612 string AC自动机 + 树状数组

时间:2023-03-22 09:14:59浏览次数:60  
标签:std AC string int acam tr 牛客 str 字符串

传送门

题目大意

  有T组测试数据,对于每组测试时局有一个n和m,n表示初始拥有的字符串数量,m表示操作数量。紧接着输入n个字符串,再读入m行操作,每行以x str的形式给出,如果x为1则是往所拥有的字符串内插入str,若x为2则是查询当前字符串包括了多少完整的字符串(重复出现也算)。

  如果要查询一个字符串被另一个字符串完整包含了多少次,可以想到AC自动机的FAIL结点的子树上的后缀都是以当前当前结点为前缀的。但是AC自动机是离线型数据结构,所以我们需要读入所有的插入字符串,然后考虑用其他数据结构动态维护每个字符串出现的次数。这里可以使用树状数组进行维护。

  我们知道如果AC自动机FAIL树上的一个结点数量增加了实际上是给当前子树内所有结点出现的次数都增加了,所有我们用树状数组进行差分动态维护子树信息。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
 
#define ll long long
#define fs first
#define se second
#define AC main(void)
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);

const long double eps = 1e-9;
const int N = 2e5 + 10, M = 2e5 + 10;

int n , m, _;

struct Fenwick{
	int maxm, cnt = 0;
	std::vector<int> tr;
	Fenwick(int n): tr(n + 1, 0) {maxm = n;}
	inline int lowbit(int x) {return x & -x;}
	
	inline void add(int x, int v){
       	for(int i = x; i <= maxm; i += lowbit(i))	tr[i] += v;
    	cnt += v;
	}
    
    inline int query(int x){
        int res = 0;
        for(int i = x; i; i -= lowbit(i)) res += tr[i];
        return res;
    }
	
	inline int query(int l, int r){
		return query(r) - query(l - 1);
	}
	
	inline int find_kmin(int k) {
        int ans = 0, cnt = 0;
        for (int i = 20; i >= 0; i--) {
            ans += (1 << i);
            if (ans >= maxm || cnt + tr[ans] >= k) ans -= (1 << i);
            else cnt += tr[ans];
        }
        return ans + 1;
    }

    inline int find_kmax(int k) {
        return find_kmin(cnt - k + 1);
    }
};

struct Aho_Corasick_Automaton{
	int cnt[N], tr[N][26], idx, q[N], nxt[N], mx[N];
	int pre[N];//跳到上一个有终止节点的位置
	int son[N];//
	int id[N], reid[N];//每个字符串原串位置的标记
	int sz[N], h[N], e[N], ne[N], dfn[N], tdl, idx1;//ac自动机fail树上建dfs序数组
	int vis[N * 2], used[N * 2];//找环
	int sigma = 26;
	int simple = 'a';

	inline void add(int a, int b){
		ne[idx1] = h[a], e[idx1] = b, h[a] = idx1 ++;
	}

	//多组测试清空操作
	inline void init(){
        for(int i = 0; i <= idx; i ++ ){
        	memset(tr[i], 0, sizeof tr[i]);
        	nxt[i] = cnt[i] = ne[i] = 0, h[i] = -1;
			vis[i] = used[i] = 0;
        } 
        idx = tdl = idx1 = 0;
    }

	inline bool findcycle(int u){//AC自动机找从0号是否可以有环(不能经过字符串被标记的地方)
		if(used[u] == 1)	return true;
		if(used[u] == -1)	return false;
		vis[u] = used[u] = true;
		for(int i = 0; i < 2; i ++)
			if(!son[tr[u][i]])	if(findcycle(tr[u][i]))	return true;
		used[u] = -1;
		return false;
	}

	inline void dfs(int u){//dfs序
		sz[u] = 1, dfn[u] = ++ tdl;
		for(int i = h[u]; ~i; i = ne[i]){
			int j = e[i];
			dfs(j);
			sz[u] += sz[j];
		}
	}
	
	inline int insert(std::string &s){//插入字符 和插入的是第几个字符
		int p = 0;
		for(char &ch : s){
			int u = ch - simple;
			if(!tr[p][u]){
				tr[p][u] = ++ idx;
			}
			p = tr[p][u];
		}
		//cnt[p] ++;
		return p;
	}

	inline int insert(std::string &s, int x){//插入字符 和插入的是第几个字符
		int p = 0;
		for(char &ch : s){
			int u = ch - simple;
			if(!tr[p][u]){
				tr[p][u] = ++ idx;
			}
			p = tr[p][u];
		}
		id[p] = x;//标记第x个字符的结尾
		reid[x] = p;
		cnt[p] ++;
		son[p] = 1;
		mx[p] = std::max(mx[p], (int)s.length());
		return p;
	}
	
	inline void build(){//建立ac自动机
		int p = 0;
		int hh = 0,tt = -1;
		for(int i = 0; i < sigma; i ++)
			if(tr[p][i])	q[++ tt] = tr[p][i];
		while(hh <= tt){
			int tq = q[hh ++];
			for(int i = 0; i < 26; i ++){
				int j = tr[tq][i];
				if(!tr[tq][i]){
					tr[tq][i] = tr[nxt[tq]][i];
				}
				else{
					q[++ tt] = tr[tq][i];
					nxt[j] = tr[nxt[tq]][i];
					if(cnt[nxt[j]])  pre[j] = nxt[j];
                   	else pre[j] = pre[nxt[j]];
					//son[j] |= son[nxt[j]];//标记能到达终止节点路径上的所有点
				}
			}
		}
	}

	//ac自动机fail树上建dfs序的建边
	inline void failtree(){
		memset(h, -1, sizeof h);
		for(int i = 1; i <= idx; i ++)	add(nxt[i], i);
		dfs(0);
	}

	inline int query(std::string &s){
		int res = 0, j = 0;
		for(char &ch : s){
			int u = ch - 'a';
			j = tr[j][u];
			int p = u;
			while(p){
				res += cnt[p];
				p = nxt[p];
			}
		}
		return res;
	}	

}acam;

inline void solve(){
	acam.init();
	std::cin >> n >> m;
	std::vector<std::string> start(n);
	std::vector<std::pair<int, std::string>> g(m + 1);
	std::string str;
	for(int i = 1; i <= n; i ++){
		std::cin >> str;
		start[i - 1] = str;
		acam.insert(str);
	}

	for(int i = 1; i <= m; i ++){
		int op;
		std::cin >> op >> str;
		g[i] = {op, str};
		if(op == 1)	acam.insert(str);
	}
	
	acam.build();
	acam.failtree();

	const auto tdl = acam.tdl;
	const auto &tr = acam.tr;
	const auto &sz = acam.sz, &dfn = acam.dfn;

	Fenwick fk(tdl);
	
	for(int i = 0; i < n; i ++){
		int p = 0;
		auto &s = start[i];
		for(int j = 0; s[j]; j ++)
			p = tr[p][s[j] - 'a'];
		
		fk.add(dfn[p], 1);
		fk.add(dfn[p] + sz[p], -1);
	}
	
	for(int i = 1; i <= m; i ++){
		auto &op = g[i].fs;
		auto &s = g[i].se;
		if(op == 1){
			int p = 0;
			for(int j = 0; s[j]; j ++)
				p = tr[p][s[j] - 'a'];
			fk.add(dfn[p], 1);
			fk.add(dfn[p] + sz[p], -1);
		}else{
			ll ans = 0;
			int p = 0;
			for(int j = 0; s[j]; j ++){
				p = tr[p][s[j] - 'a'];
				ans += fk.query(dfn[p]);
			}
			std::cout << ans << '\n';
		}
	}
}

signed AC{
   	

   	std::cin >> _;
	while(_ --)
    	solve();

    return 0;
}

标签:std,AC,string,int,acam,tr,牛客,str,字符串
From: https://www.cnblogs.com/qdhys/p/17242320.html

相关文章

  • springcloud学习——nacos
    1介绍nacos是阿里巴巴开发的,现在已属于springcloud框架,功能比eureka更加丰富2安装与启动下载:GitHub主页:https://github.com/alibaba/nacos解压安装包后,在bin文件夹中......
  • 在 React 组件中使用 JSON 数据文件,怎么去读取请求数据呢?
    要在React组件中使用JSON数据,有多种方法。常用的有以下几种方法:1、直接将JSON数据作为一个变量或常量引入组件中。importjsonDatafrom'./data.json';functio......
  • 微信助手 Mac上好用的一款微信插件
    偶然间在GitHub上看到一个非常好的插件附上插件给大家学习使用更多功能及介绍可查看安装及详细功能介绍迷离/黑夜/上帝/少女皮肤模式少量细节没有做适配,主题模式-关......
  • macOS系统mamp搭建php连接sqlServer扩展,php连接sqlserver数据库
    macOS系统mamp搭建php连接sqlServer扩展,php连接sqlserver数据库下载:github上提供已经打包的os拓展文件https://github.com/Microsoft/msphpsql/releases打开php......
  • anaconda安装
    一般直接下一步下一步,在这里直接简述可能遇到问题的注意点1.安装anaconda,在这个步骤和python安装是有关系的,如果忘记了,这里可以记住当前选择项,文章末尾会验证此处是否正确 ......
  • 牛客挑战赛67 B数据结构
    牛客挑战赛67B数据结构你有一个长度为n的字符串,其中包含'0','1','2'三种字符。问字符串中有多少个字串满足'0','1','2'三种字符数量相等。\(1<=n<=3e5\)一开始想了......
  • 【macOS游戏】战锤40000:机械师
    原文来源于黑果魏叔官网,转载需注明出处。控制帝国技术最先进的军队之一-AdeptusMechanicus。作为多米努斯·福斯蒂尼乌斯的魔术师,带领一支探险队前往新发现的necronSilva......
  • Creating VM fails with error: "No VASA Provider for schema namespace (VSAN) foun
    ​​https://kb.vmware.com/s/article/52286​​......
  • 6-1 使用函数输出指定范围内Fibonacci数的个数
    本题要求实现一个计算Fibonacci数的简单函数,并利用其实现另一个函数,输出两正整数m和n(0<m<n≤100000)之间的所有Fibonacci数的数目。所谓Fibonacci数列就是满足任一项数字是......
  • CentOS7安装mysql提示“No package mysql-server available
    在CentOS7上安装mysql时,出现了以下的提示:原因是:CentOS7带有MariaDB而不是MySQL,MariaDB和MySQL一样也是开元的数据库,您可以使用yum-yinstall mariadb-servermariadb命令......