[BZOJ 3942 ]KMP+栈  [BZOJ3940]AC自动机+栈

BZOJ3942
 分析:一个个匹配,不过中途记录一下当前的fail, 这样的话删除一部分,也可以回溯到那时的状态

 
/***********************************************
Author        :lzs
Created Time  :2018年10月23日 星期二 20时08分00秒
File Name     :bzoj_3942.cpp
************************************************/
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)
#define dbgln(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<"\n"
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"
 
 
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
 
const int N = (int) 1e6 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/
 
char s[N], t[N];
int nxt[N];
void getnxt(char *s){
    int i, j;
    j = nxt[0] = -1;
    i = 0; int n = strlen(s);
    while(i < n){
        while(j != -1 && s[i] != s[j]) j = nxt[j];
        ++j; ++i;
        if(s[j] == s[i]) nxt[i] = nxt[j];
        else nxt[i] = j;
    }
}
int fail[N], top;
char ans[N];
int main(){
    scanf("%s%s", s, t);
    stack<char>st;
    getnxt(t);
    int j = 0; int lent = strlen(t);
    for(int i = 0; s[i]; ){
        ans[++top] = s[i];
        while(j != -1 && t[j] != s[i]) j = nxt[j];
        ++j; ++i; 
        fail[top] = j; 
        if(j == lent) {
            top -= lent;
            j = fail[top]; 
        }
    }
    for(int i = 1; i <= top; i++) putchar(ans[i]);putchar('\n');
    return 0;
}

BZOJ3940
和上一道思路一样,匹配中途维护一下当前的fail值,这样删除后,才可以回溯到原来的状态
一个模式串用kmp就可以,但是多个模式串肯定要用ac自动机了

#include
using namespace std;
const int N = (int)1e5 + 11;
queue<int>q;
struct AC_{
	// 字典树相关,分别表示: 存储图,标记单词尾,总节点个数
	int c[N][26], val[N], cnt;
	int len[N];	
	int fail[N]; // 失配转移 
	int f[N];
	char ans[N]; int top;
	void ins(char *s){
		int le=strlen(s);int now=0;
		for(int i=0;i<le;i++){
			int v=s[i]-'a';
			if(!c[now][v])c[now][v]=++cnt;
			now=c[now][v];
		}
		val[now]++; //标记 是一个单词尾
		len[now] = le;
	}
	void build(){ // fail的建立, 注意fail建立之后 字典树中的指向已经变了
		// 和根节点相连的加入队列中
		for(int i=0;i<26;i++) if(c[0][i])fail[c[0][i]]=0,q.push(c[0][i]);
		while(!q.empty()){
			int u=q.front();q.pop();
			for(int i=0;i<26;i++)
				if(c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);//如果u有i这个分支
				else c[u][i]=c[fail[u]][i]; // 可以跳很多
		}
	}
	void query(char *s){
		int le=strlen(s);
		int now=0;
		for(int i=0;i<le;i++){
			ans[++top] = s[i];
			now=c[now][s[i]-'a'];
			// val[t] = -1 表示已经访问过, t = 0表示到了根节点
			f[top] = now;
			if(val[now]){ //核心!因为模式串之间不会有互为子串,所以不用沿着fail下找
				// 这样才不会TLE
				top -= len[now];
				now = f[top];	
				continue;
			}		
		}
		for(int i = 1; i <= top; i++) putchar(ans[i]);putchar('\n');
	}
}AC;
int n;char p[1000005];
char s[N];
int main(){
	scanf("%s", p);
	int n; scanf("%d" ,&n);
	for(int i = 0; i < n; i++){
		scanf("%s", s);
		AC.ins(s);
	}
	AC.build();
	AC.query(p);
	return 0;
}

你可能感兴趣的:(AC-,自动机,KMP)