首页 > 其他分享 >AC自动机与dp详解

AC自动机与dp详解

时间:2023-10-06 22:46:12浏览次数:33  
标签:AC int 字符串 fail 自动机 dp

AC自动机与dp

前言:

本篇题解隶属于https://www.cnblogs.com/linghusama/p/17742870.html部分
首先一定要理解fail跳的原理,不然很难理解第二维为什么要设置。
首先给出大致的雏形,dp_i_j表示目前拼凑出长度为i的字符串,且ac自动机上的指针在j位置时的字符串方案数。

适用题型:

对于求必须包含、不包含某些模式串,且长度一定的合法字符串数量

以下以模版题[JSOI2007] 文本生成器为例子。
容斥原理部分不再赘述。

这里我用dp三大组成部分分别进行考虑

对于状态设计

最基础的就是两个维度,一个表示做了多长的字符串,一个表示此时自动机的位置。

对于转移:

我们考虑怎么转移到i+1这个情况上来。
首先因为加了一个新的字符,我们设它为c,那么按照自动机而言,他会先在j这个位置搜索j的儿子一圈看看能不能匹配到c。
然后我们分类讨论一下转移的思路。

  1. 如果找到了c就是他的儿子的话,而且c是作为一个模式串的结尾,那么就说明不能填充c这个东西。
  2. 如果找到了c就是他的儿子的话,而且c不是任何一个模式串的结尾,那么填充了c还是能保证不会生成出模式串,所以可以填充。
  3. 如果没有找到c是他的儿子的话,那么现在我要跳fail去找,此时j进行了变化,然后继续重新分类讨论。
  4. 如果fail跳不动了(到根节点了),说明这么多种情况一个都没有用,全部都不合法,为0。
    对于第四点,我们可以发现,如果一个节点必须要跳fail时而且fail无解时,自己也无解,这里可以在build时递归处理。
    具体怎么做,我们先把不合法转移,也就是下一次会转移到模式串尾巴的地方给打上标记,所有能跳到这个位置的就利用或运算来解决

对于初始化:

为了便于决定第一个位置的c该怎么取,可以让第0位的方案数为1,便于计算。

例题代码:

#include<bits/stdc++.h>
using namespace std;
int tree[30006][30];
int cnt=0;
const int mod=1e4+7;
bool nosol[6500];
int fail[6500];
void add(string s){
	int sz=s.size();
	int p=0;
	for(int i=0;i<sz;i++){
		int c=s[i]-'A';
		if(!tree[p][c]){
			cnt++;
			tree[p][c]=cnt;
		}
		p=tree[p][c];
	}
	nosol[p]=1;
}
void build(){
	queue<int>q;
	for(int i=0;i<26;i++){
		if(tree[0][i]){
			q.push(tree[0][i]);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			if(tree[u][i]){
				
				fail[tree[u][i]]=tree[fail[u]][i];
				nosol[tree[u][i]]|=nosol[fail[tree[u][i]]];//一个不合法全部不合法。
				q.push(tree[u][i]); 
			}
			else{
				tree[u][i]=tree[fail[u]][i];
			}
		}
	}
}
int dp[105][6500];
int qpow(int a,int b){
	int ret=1;
	a%=mod;
	while(b){
		if(b%2==1){
			ret=ret*a%mod;
		}
		b/=2;
		a=a*a%mod;
	} 
	return ret;
}
int main(){
	ios::sync_with_stdio(false);
	int n,m;
	cin >> n >>m;
	for(int i=1;i<=n;i++){
		string s;
		cin >>s;
		add(s);
	}
	build();
//	cout<<tree[2][1];
	dp[0][0]=1;
	for(int i=0;i<=m-1;i++){
		for(int j=0;j<=cnt;j++){
			for(int c=0;c<=25;c++){
				if(!nosol[tree[j][c]]){
					dp[i+1][tree[j][c]]=(dp[i+1][tree[j][c]]+dp[i][j])%mod;
				}
					
			}
		}
	}
	int ans=qpow(26,m);
//	cout<<ans<<endl;
	for(int i=0;i<=cnt;i++)
    	ans=(ans-dp[m][i]+mod)%mod;  
    cout<<ans;
	
}

标签:AC,int,字符串,fail,自动机,dp
From: https://www.cnblogs.com/linghusama/p/17745214.html

相关文章

  • 树形DP
    目录树形DP例题洛谷P1352没有上司的舞会树形DP在树上跑DP例题洛谷P1352没有上司的舞会......
  • 前端设计模式:工厂模式(Factory)
    00、基础概念......
  • 一些转移细节还不太清楚的线性dp
    D.RoundSubset老早写过了,但是边界考虑不太清楚https://codeforces.com/problemset/problem/837/D#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintN=205,M=30*200;intn,k,ans,t2[N],t5[N],f[2][N][M];//f[i][j]:选了i个,5......
  • React 路由
    React路由1.ReactRouter5.x功能概述:点击切换展示区内容,并切换浏览器地址/about/homeAbout组件importReact,{Component}from'react'exportdefaultclassAboutextendsComponent{render(){return(<h3>我是About的内容</h3>......
  • 2022 CCPC 威海 ACEGJ
    2022ChinaCollegiateProgrammingContest(CCPC)WeihaiSiteACEGJA.Dunai思维题意:之前有\(n\)场比赛,有\(n\)个冠军队伍,每个队伍5个人。接下来给你\(m\)个即将参加比赛的人和所在位置(1~5)。问你在保证一个队伍至少有一个冠军在,并且每个位置都要有人。问最多能组成......
  • G9、ACGAN理论与实战
    ......
  • ACL(控制列表)的命令的表达方式
    扩展访问控制列表的配置 创建ACLRouter(config)#access-list access-list-number{permit|deny}protocol{sourcesource-wildcarddestination destination-wildcard}[operatoroperan] 2.删除ACL Router(config)#noaccess-listaccess-list-number3.将ACL应......
  • 背包DP
    题目背景:你有一个容量为M的背包,有N个物品,每个物品的重量和价值分别为w[i]和c[i],现在选一些物品放入这个背包使装入的价值最大1.01背包(每件物品只有1件):倒序枚举重量,O(N^2) E(i,n){ cin>>w>>c; for(intj=m;j>=w;--j)f[j]=max(f[j],f[j-w]+c); }2.完全背包(每件物品......
  • Topaz Video AI:智能重塑视频画质,引领视觉体验升级 Mac+win版
    探索TopazVideoAI如何通过智能技术重塑您的视频画质,全面提升视觉体验。→→↓↓载TopazVideoAImac/win版TopazVideoAI是一款领先的智能视频修复软件,专为提升视频画质而生。通过对AI技术的深度集成,它可以帮助您将老旧、低分辨率的视频进行智能修复和增强,带来焕然一新的视......
  • ADG环境RAC主库在清理归档时出现RMAN-08120
    1、环境信息11gRAC+单节点ADG2、目的清理部分已应用过的归档,且清理之前保证主库所有归档已被应用3、异常信息--定时任务在清理过期归档时出现,但DG日志应用是正常的RMAN-08120:WARNING:archivedlognotdeleted,notyetappliedbystandbyarchivedlogfilename=+ARCH/t......