BZOJ2555: SubString

2555: SubString

Description

  
    懒得写背景了,给你一个字符串init,要求你支持两个操作
    
    (1):在当前字符串的后面插入一个字符串
    
    (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
    
    你必须在线支持这些操作。
    

Input

    第一行一个数Q表示操作个数
    
    第二行一个字符串表示初始字符串init
    
    接下来Q行,每行2个字符串Type,Str 
    
    Type是ADD的话表示在后面插入字符串。
    
    Type是QUERY的话表示询问某字符串在当前字符串中出现了几次。
    
    为了体现在线操作,你需要维护一个变量mask,初始值为0
   
    
    读入串Str之后,使用这个过程将之解码成真正询问的串TrueStr。
    询问的时候,对TrueStr询问后输出一行答案Result
    然后mask = mask xor Result  
    插入的时候,将TrueStr插到当前字符串后面即可。

HINT:ADD和QUERY操作的字符串都需要解压
   

Output

Sample Input

2

A

QUERY B

ADD BBABBBBAAB

Sample Output


0

HINT

 40 % 的数据字符串最终长度 <= 20000,询问次数<= 1000,询问总长度<= 10000

    

100 % 的数据字符串最终长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000


新加数据一组--2015.05.20

   



询问一个串在母串中的出现次数

即 询问该串在母串的后缀自动机上所到达状态的Right集合的大小

后缀自动机上一个状态的Right集合大小即为该状态在parent树中所有儿子的Right的和

即该状态的叶子节点个数(叶子节点的Right集合大小为1)

对母串建后缀自动机,LCT动态维护每个状态的Right集合大小即可


#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
using namespace std;
const int MaxN = 3000003;
const int MaxC = 600006;
int n, mask;
char s[MaxN];
namespace Link_Cut_Tree {
	struct point {
		int l, r, fa;
		int key, add;
		inline point () {};
		inline point (const int &x) {
			l = r = fa = 0;
			add = 0, key = x;
		}
		#define fa(x) P[x].fa
		#define l(x) P[x].l
		#define r(x) P[x].r
		#define key(x) P[x].key
		#define add(x) P[x].add
	}P[MaxC << 1];
	int que[MaxC << 1], qr;
	inline bool isRoot(const int &x) {
		return !fa(x) || l(fa(x)) != x && r(fa(x)) != x;
	}
	inline bool which(const int &x) {
		return r(fa(x)) == x;
	}
	inline void Add(const int &x, const int &y) {
		key(x) += y, add(x) += y;
	}
	inline void Down(const int &x) {
		if (add(x)) {
			if (l(x)) Add(l(x), add(x));
			if (r(x)) Add(r(x), add(x));
			add(x) = 0;
		}
	}
	inline void Turn(const int &x) {
		int y = fa(x), z = fa(y), w = (l(y) == x ? r(x) : l(x));
		fa(x) = z; if (!isRoot(y)) (l(z) == y ? l(z) : r(z)) = x;
		fa(y) = x, (l(y) == x ? r(x) : l(x)) = y;
		if (w) fa(w) = y; (l(y) == x ? l(y) : r(y)) = w;
	} 
	inline void Splay(const int &x) {
		que[qr = 0] = x;
		for (int y = x; !isRoot(y); y = fa(y))
			que[++qr] = fa(y);
		for (; qr >= 0; --qr) Down(que[qr]);
		while (!isRoot(x)) {
			if (!isRoot(fa(x))) {
				if (which(fa(x)) == which(x)) Turn(fa(x));
				else Turn(x);
			}
			Turn(x);
		}
	}
	inline void Acess(const int &x) {
	     int y = 0, z = x;
		 for (; z; y = z, z = fa(z))
			 Splay(z), r(z) = y;
		 Splay(x);
	}
	inline void Link(const int &x, const int &y) {
		fa(x) = y, Acess(y), Add(y, key(x));
	}
	inline void Cut(const int &x) {
		Acess(x), Add(l(x), -key(x));
		fa(l(x)) = 0, l(x) = 0;
	}
}
inline void Get(int mask) {
    scanf("%s", s);
	int len = strlen(s);
	for (int i = 0; s[i]; ++i) {
		mask = (mask * 131 + i) % len;
		swap(s[i], s[mask]);
	}
}
#define LCT Link_Cut_Tree
namespace Suffix_Automaton {
	int root, last, cnt;
	struct SAM {
		int to[26];
		int Max, par;
		#define Max(x) A[x].Max
		#define par(x) A[x].par
	}A[MaxC << 1];
	inline void Init(const int &x, const int &m, const int &w) {
		for (int i = 0; i < 26; ++i)
			A[x].to[i] = 0;
		Max(x) = m;
		LCT :: P[x] = LCT :: point(w);
	}
	inline void SAMInit() {
		root = last = cnt = 1;
		Init(1, 0, 0);
	}
	inline void Extend(const int &c) {
		int p = last, np = last = ++cnt;
		Init(np, Max(p) + 1, 1);
	    while (p && !A[p].to[c]) A[p].to[c] = np, p = par(p);
		if (!p) par(np) = root, LCT :: Link(np, root);
		else {
		    int q = A[p].to[c];
			if (Max(q) == Max(p) + 1) par(np) = q, LCT :: Link(np, q);
			else {
				int nq = ++cnt;
				Init(nq, Max(p) + 1, 0);
				for (int i = 0; i < 26; ++i)
					A[nq].to[i] = A[q].to[i];
				par(nq) = par(q), LCT :: Link(nq, par(q));
				LCT :: Cut(q), LCT :: Link(q, nq), LCT :: Link(np, nq);
				par(np) = par(q) = nq;
				while (A[p].to[c] == q) A[p].to[c] = nq, p = par(p);
			}
		}
	}
	inline void Build() {
		SAMInit();
	    scanf("%s", s);
        for (int i = 0; s[i]; ++i)
			Extend(s[i] - 'A');			
	}
	inline void Insert() {
		Get(mask);
        for (int i = 0; s[i]; ++i)
			Extend(s[i] - 'A');		
	}
	inline int Query() {
		Get(mask);
		int u = root;
		for (int i = 0; s[i]; ++i) {
			int c = s[i] - 'A';
			if (!A[u].to[c]) return 0;
			else u = A[u].to[c];
		}
		LCT :: Splay(u);
		return LCT :: key(u);
	}
}
int main()
{
	scanf("%d", &n);
	Suffix_Automaton :: Build();
	for (int i = 1; i <= n; ++i) {
		scanf("%s", s);
		if (s[0] == 'A') Suffix_Automaton :: Insert();
		else {
			int res = Suffix_Automaton :: Query();
			mask ^= res;
			printf("%d\n", res);
		}
	}
}#include 
        
        
         
         
#include 
         
         
           #include 
          
            using namespace std; const int MaxN = 3000003; const int MaxC = 600006; int n, mask; char s[MaxN]; namespace Link_Cut_Tree { struct point { point *l, *r, *fa; int key, add; point (); }*null = new point; inline point :: point () { l = r = fa = null; add = key = 0; } point *que[MaxC << 1]; int qr; inline bool isRoot(point *x) { return !x -> fa || x -> fa -> l != x && x -> fa -> r != x; } inline bool which(point *x) { return x -> fa -> r == x; } inline void Add(point *x, const int &y) { x -> key += y, x -> add += y; } inline void Down(point *x) { if (x -> add) { if (x -> l) Add(x -> l, x -> add); if (x -> r) Add(x -> r, x -> add); x -> add = 0; } } inline void Turn(point *x) { point *y = x -> fa, *z = y -> fa; point *w = y -> l == x ? x -> r : x -> l; x -> fa = z; if (!isRoot(y)) (z -> l == y ? z -> l : z -> r) = x; y -> fa = x, (y -> l == x ? x -> r : x -> l) = y; if (w) w -> fa = y; (y -> l == x ? y -> l : y -> r) = w; } inline void Splay(point *x) { que[qr = 0] = x; for (point *y = x; !isRoot(y); y = y -> fa) que[++qr] = y -> fa; for (; qr >= 0; --qr) Down(que[qr]); while (!isRoot(x)) { if (!isRoot(x -> fa)) { if (which(x -> fa) == which(x)) Turn(x -> fa); else Turn(x); } Turn(x); } } inline void Acess(point *x) { point *y = null, *z = x; for (; z; y = z, z = z -> fa) Splay(z), z -> r = y; Splay(x); } inline void Link(point *x, point *y) { x -> fa = y, Acess(y), Add(y, x -> key); } inline void Cut(point *x) { Acess(x), Add(x -> l, -x -> key); x -> l -> fa = 0, x -> l = 0; } } inline void Get(int mask) { scanf("%s", s); int len = strlen(s); for (int i = 0; s[i]; ++i) { mask = (mask * 131 + i) % len; swap(s[i], s[mask]); } } #define LCT Link_Cut_Tree namespace Suffix_Automaton { struct SAM { SAM *to[26], *par; LCT :: point *tree; int Max; SAM (const int &M = 0, const int &w = 0) { for (int i = 0; i < 26; ++i) to[i] = NULL; par = NULL; Max = M; tree = new LCT :: point; tree -> key = w; } }*root = new SAM, *last = root; inline void Extend(const int &c) { SAM *p = last, *np = new SAM(p -> Max + 1, 1); while (p && !p -> to[c]) p -> to[c] = np, p = p -> par; if (!p) np -> par = root, LCT :: Link(np -> tree, root -> tree); else { SAM *q = p -> to[c]; if (q -> Max == p -> Max + 1) np -> par = q, LCT :: Link(np -> tree, q -> tree); else { SAM *nq = new SAM(p -> Max + 1, 0); for (int i = 0; i < 26; ++i) nq -> to[i] = q -> to[i]; nq -> par = q -> par, LCT :: Link(nq -> tree, q -> par -> tree); LCT :: Cut(q -> tree), LCT :: Link(q -> tree, nq -> tree), LCT :: Link(np -> tree, nq -> tree); np -> par = q -> par = nq; while (p && p -> to[c] == q) p -> to[c] = nq, p = p -> par; } } last = np; } inline void Insert() { for (int i = 0; s[i]; ++i) Extend(s[i] - 'A'); } inline int Query() { SAM *u = root; for (int i = 0; s[i]; ++i) { int c = s[i] - 'A'; if (!u -> to[c]) return 0; else u = u -> to[c]; } LCT :: Splay(u -> tree); return u -> tree -> key; } } int main() { scanf("%d", &n); scanf("%s", s); Suffix_Automaton :: Insert(); for (int i = 1; i <= n; ++i) { scanf("%s", s); if (s[0] == 'A') { Get(mask); Suffix_Automaton :: Insert(); } else { Get(mask); int res = Suffix_Automaton :: Query(); mask ^= res; printf("%d\n", res); } } } 
           
         
        
        
       
       
      
      
     
     


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值