2555: SubString

2555: SubString

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 1910   Solved: 539
[ Submit][ Status][ Discuss]

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

   

Source

[ Submit][ Status][ Discuss]

使用后缀自动机+LCT解决,,传送门
后缀自动机方面看CLJ的ppt啦。。。
后缀自动机里每个节点存一个子串,该串是从init开始,每次走添加字符边形成的,新字符放在后面
这个点同时维护这个子串在母串中出现位置的集合
然后定义parent边,par(s)为字符串x,满足s出现集合是x集合的真子集,且x的size最小
每个点都有一个长度,对于节点s,该串的长度在(len(par(s)),len(s)]的后缀都满足当前点维护的位置集合
构造方法见论文吧。。。。。
因为这个后缀自动机是个动态结构,所以可以用LCT维护
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E6 + 2E5 + 233;

int n,Q,mas,rt,last,cnt,len[maxn],par[maxn],g[maxn][26],sum[maxn]
	,fa[maxn],pfa[maxn],ch[maxn][2],rev[maxn],Add[maxn];
char c[maxn];

stack <int> st;

void pushdown(int x)
{
	if (rev[x]) 
	{
		swap(ch[x][0],ch[x][1]);
		rev[x] ^= 1;
		for (int i = 0; i < 2; i++)
			if (ch[x][i]) rev[ch[x][i]] ^= 1;
	}
	if (Add[x] != 0)
	{
		sum[x] += Add[x];
		for (int i = 0; i < 2; i++)
			if (ch[x][i] != 0) Add[ch[x][i]] += Add[x];
		Add[x] = 0;
	}
}

void rotate(int x)
{
	int y = fa[x],z = fa[y];
	int d = (ch[y][0] == x)?0:1;
	fa[x] = z; fa[y] = x;
	pfa[x] = pfa[y]; pfa[y] = 0;
	ch[y][d] = ch[x][d^1];
	if (ch[y][d]) fa[ch[y][d]] = y;
	ch[x][d^1] = y;
	if (z) ch[z][ch[z][1] == y] = x;
}

void splay(int x)
{
	for (int z = x; z; z = fa[z]) st.push(z);
	while (!st.empty()) pushdown(st.top()),st.pop();
	for (int y = fa[x]; y; rotate(x),y = fa[x])
		if (fa[y])
			rotate((ch[fa[y]][0] == y)^(ch[y][0] == x)?x:y);
}

void Access(int x)
{
	for (int v = 0; x; v = x,x = pfa[x])
	{
		splay(x);
		if (ch[x][1]) fa[ch[x][1]] = 0,pfa[ch[x][1]] = x;
		if (v != 0) pfa[v] = 0,fa[v] = x;
		ch[x][1] = v;
	}
}

void ChangeRoot(int x)
{
	Access(x); splay(x);
	rev[x] ^= 1;
}

void Join(int x,int y)
{
	ChangeRoot(x); pfa[x] = y;
	Access(x); splay(x);
}

void Cut(int x,int y)
{
	ChangeRoot(x);
	Access(y); splay(y);
	fa[x] = ch[y][0] = 0;
}

void LCT_join(int x,int y)
{
	Join(x,y); ChangeRoot(rt);
	Access(y); splay(y);
	sum[y] += sum[x];
	if (ch[y][0]) Add[ch[y][0]] += sum[x]; 
}

void LCT_cut(int x,int y)
{
	Cut(x,y); ChangeRoot(rt);
	Access(y); splay(y);
	sum[y] -= sum[x];
	if (ch[y][0]) Add[ch[y][0]] -= sum[x];
}

int New_node(int le,int va)
{
	int ret = ++cnt; len[ret] = le;
	sum[ret] = va; return ret;
}

void Extend(int w)
{
	int p = last,np = New_node(len[p] + 1,1);
	while (p && !g[p][w])
		g[p][w] = np,p = par[p];
	if (!p)
	{
		par[np] = rt;
		LCT_join(np,rt);
		last = np; return;
	}
	
	int q = g[p][w];
	if (len[p] + 1 == len[q])
		par[np] = q,LCT_join(np,q);
	else
	{
		int nq = New_node(len[p] + 1,0);
		for (int i = 0; i < 26; i++) g[nq][i] = g[q][i];
		par[nq] = par[q]; par[q] = par[np] = nq;
		LCT_cut(q,par[nq]); LCT_join(q,nq);
		LCT_join(np,nq); LCT_join(nq,par[nq]);
		while (p && g[p][w] == q)
			g[p][w] = nq,p = par[p];
	}
	last = np;
}

int Getcom(int mas)
{
	scanf("%s",c);
	int ret = (c[0] == 'A')?1:2;
	scanf("%s",c); n = strlen(c);
	for (int i = 0; i < n; i++)
	{
		mas = (mas*131 + i) % n;
		swap(c[i],c[mas]);
	}
	return ret;
}

int main()
{
	
	last = rt = New_node(0,0); cin >> Q; 
	scanf("%s",c); n = strlen(c);
	for (int i = 0; i < n; i++) Extend(c[i] - 'A');
	while (Q--)
	{
		int typ = Getcom(mas);
		if (typ == 1)
		{
			for (int i = 0; i < n; i++) Extend(c[i] - 'A');
		}
		else
		{
			int s = rt;
			for (int i = 0; i < n && s; i++) s = g[s][c[i] - 'A'];
			if (!s) {puts("0"); continue;}
			Access(s); splay(s);
			mas ^= sum[s];
			printf("%d\n",sum[s]);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值