zoukankan      html  css  js  c++  java
  • changeeksdja

    这几天都在做\(AC\)自动姬,我怀着逃避的心态做前面的字符串专题,然后,然后,这\(nm\)还是\(AC\)自动姬!(好吧其实这题可以用\(trie\)直接做,可是我已经走火入魔了)

    我们先建一颗\(trie\)树,那么树中的每个节点显然都满足前缀的最后一个点。

    由于\(AC\)自动姬的性质,我们知道一个节点的\(fail\)必然是树上的\(depth\)离自己最近且字母相同的一个点。

    比如有\(abc\)\(c\)两个串插入。\(a\)\(fail\)\(root\)\(b\)\(fail\)\(root\),第一个\(c\)\(fail\)就是第二个\(c\)。假设第三个串是\(ab\),那么第一个\(b\)\(fail\)就是第二个\(b\)了。符合上述结论。

    我们将每个点与其\(depth\)相差为\(1\)\(fail\)连一条边,表示这两个点只能选一个(相差的正好是首字母)。然后大力\(DP\)即可。

    证明一下为什么不会有环:每次都是\(depth\)相差\(1\),是不可能有环的。

    又到了激动人心的上代码环节。

    #include<queue>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    using namespace std;
    
    int T, n;
    queue <int> q;
    char s[1000002];
    
    struct Trie {
    	int fail[1000002], ch[1000002][30], cnt;
    	int newnode() {
    		for(int i = 0; i < 26; ++ i) ch[cnt][i] = 0;
    		++ cnt;
    		return cnt - 1;
    	}
    	void clear() {
    		cnt = tot = 0;
    		newnode();
    	}
    	void insert(const int len) {
    		int p = 0, depth = 0;
    		for(int i = 0; i < len; ++ i) {
    			int op = s[i] - 'a';
    			++ depth;
    			if(! ch[p][op]) ch[p][op] = newnode(), dep[ch[p][op]] = depth;
    			p = ch[p][op];
    		}
    	}
    	void getfail() {
    		for(int i = 0; i < 26; ++ i) {
    			if(! ch[0][i]) continue;
    			fail[ch[0][i]] = 0;
    			q.push(ch[0][i]);
    		}
    		while(! q.empty()) {
    			int u = q.front(); q.pop();
    			for(int i = 0; i < 26; ++ i) {
    				if(ch[u][i]) {
    					fail[ch[u][i]] = ch[fail[u]][i];
    					q.push(ch[u][i]);
    				}
    				else ch[u][i] = ch[fail[u]][i];
    			}
    		}
    	}
    	int tot, dot[2000002], nxt[2000002], head[1000002], dep[1000002];
    	long long dp[1000002][2];
    	bool vis[1000002];
    	void addEdge(const int u, const int v) {
    		dot[++ tot] = v;
    		nxt[tot] = head[u];
    		head[u] = tot;
    	}
    	void build() {
    		for(int i = 1; i < cnt; ++ i) vis[i] = head[i] = 0;
    		for(int i = 1; i < cnt; ++ i) {
    			int fa = fail[i];
    			if(dep[fa] + 1 == dep[i] && fa) addEdge(i, fa), addEdge(fa, i);
    		}
    	}
    	void DP(const int u, const int fa) {
    		vis[u] = 1;
    		dp[u][0] = 0; dp[u][1] = 1;
    		for(int i = head[u]; i; i = nxt[i]) {
    			int v = dot[i];
    			if(v == fa) continue;
    			DP(v, u);
    			dp[u][0] += max(dp[v][0], dp[v][1]);
    			dp[u][1] += dp[v][0];
    		}
    	}
    	void solve() {
    		getfail(); build();
    		long long ans = 0;
    		for(int i = 1; i < cnt; ++ i) {
    			if(vis[i]) continue;
    			DP(i, 0);
    			ans += max(dp[i][0], dp[i][1]);
    		}
    		printf("%lld\n", ans);
    	}
    	void debug()
        {
            for(int i = 0;i < cnt;i++)
            {
                printf("id = %3d,fail = %3d,chi = [",i,fail[i]);
                for(int j = 0;j < 26;j++)
                    printf("%2d",ch[i][j]);
                printf("]\n");
            }
        }
    }ac;
    
    int read() {
    	int x = 0, f = 1; char s;
    	while((s = getchar()) > '9' || s < '0') {
    		if(s == '-') f = -1;
    		if(s == EOF) exit(0);	
    	}
    	while(s <= '9' && s >= '0') {
    		x = (x << 1) + (x << 3) + (s ^ 48);
    		s = getchar();
    	}
    	return x * f;
    }
    
    int main() {
    	T = read();
    	while(T --) {
    		n = read();
    		ac.clear();
    		for(int i = 1; i <= n; ++ i) scanf("%s", s), ac.insert(strlen(s));
    		ac.solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    [unity3d程序] 纹理扩散
    深入浅出SharePoint——自定义带ECB列
    深入浅出TFS——工作区Workspace
    深入浅出SharePoint——部署WSP
    深入浅出SharePoint——批处理高效导入数据
    深入浅出SharePoint——Log4net应用
    遍历文件夹及文件
    深入浅出SharePoint——使用CAML定制View
    拷贝文件到另一台电脑
    深入浅出SharePoint——在自定义表单中使用上传附件控件
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12358073.html
Copyright © 2011-2022 走看看