zoukankan      html  css  js  c++  java
  • [NOI2011] 阿狸的打字机

    暴力做法:
    将字符串建成Trie图。设字符串x的标记节点是idStr[x],那么对于询问(x,y),它的答案是从idStr[y]到根的路径上能够通过fail指针条到idStr[x]的节点个数。

    优化:
    将fail指针反向建立一颗fail树,那么询问(x,y)的答案为统计fail树中isStr[x]的子树内存在多少个在Trie上是idStr[y]的祖先的节点数目。这类字数问题可以使用dfs序+树状数组即可得到解决。

    请开启-std=c++11选项。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1e5+10;
    
    
    int cntStr,cntNode;
    int fa[N],fail[N],t1[N][26],t2[N][26];
    int idStr[N];
    
    int in[N],out[N],cntTime;
    vector<int> failTree[N];
    
    int bit[N],ans[N],rootQuery[N];
    vector<int> idQuery[N];
    
    void add(int x,int y) {
    	for(; x<=cntTime; x+=(x&-x)) bit[x]+=y;
    }
    int sum(int x,int y=0) {
    	for(; x>0; x-=(x&-x)) y+=bit[x];
    	return y;
    }
    
    void getOrder(int x) {
    	in[x]=++cntTime;
    	for(int y: failTree[x]) getOrder(y);
    	out[x]=cntTime;
    }
    void getAnswer(int x) {
    	add(in[x],1);
    	for(int prb: idQuery[x]) {
    		ans[prb]=sum(out[rootQuery[prb]])-sum(in[rootQuery[prb]]-1);
    	}
    	for(int i=0; i<26; ++i) {
    		if(t1[x][i]) getAnswer(t1[x][i]);
    	}
    	add(in[x],-1);
    }
    
    void func() {
    	static char str[N];
    	scanf("%s",str);
    	int p=0;
    	for(int i=0; str[i]; ++i) {
    		if(str[i]=='P') idStr[++cntStr]=p;
    		else if(str[i]=='B') p=fa[p];
    		else {
    			if(!t1[p][str[i]-'a']) {
    				t1[p][str[i]-'a']=++cntNode;
    				fa[cntNode]=p;
    			}
    			p=t1[p][str[i]-'a'];
    		}
     	}
     	// puts("builtTrie");
     	static queue<int> Q;
     	for(int i=0; i<26; ++i) {
     		if(t1[0][i]) {
     			t2[0][i]=t1[0][i];
     			Q.push(t1[0][i]);
     		}
     	}
     	while(!Q.empty()) {
     		int x=Q.front();
     		Q.pop();
     		for(int i=0; i<26; ++i) {
     			if(t1[x][i]) {
     				fail[t1[x][i]]=t2[fail[x]][i];
     				t2[x][i]=t1[x][i];
     				Q.push(t1[x][i]);
     			} else t2[x][i]=t2[fail[x]][i];
     		}
     		failTree[fail[x]].push_back(x);
     	}
     	// puts("buildACAM");
     	getOrder(0);
     	// puts("buildDFSOrder");
    }
    
    int main() {
    	func();
    	int q,x,y;
    	scanf("%d",&q);
    	for(int i=1; i<=q; ++i) {
    		scanf("%d%d",&x,&y);
    		rootQuery[i]=idStr[x];
    		idQuery[idStr[y]].push_back(i);
    	} 
    	getAnswer(0);
    	for(int i=1; i<=q; ++i) {
    		printf("%d
    ",ans[i]);
    	}
    	return 0;
    }
    
    /*
    aPaPBbP
    3
    1 2
    1 3
    2 3
    */
    
  • 相关阅读:
    四.单例模式
    三.创建型模式的特点和分类
    二.设计模式原则
    一.软件设计模式的基本要素
    jvm之年轻代(新生代)、老年代、永久代以及GC原理详解、GC优化
    Java NIO系列教程(十一) Java NIO DatagramChannel
    Java NIO系列教程(十)client和server 示例
    Java NIO系列教程(九) ServerSocketChannel
    Java NIO系列教程(八) SocketChannel
    MySQL读写分离
  • 原文地址:https://www.cnblogs.com/nosta/p/10173286.html
Copyright © 2011-2022 走看看