BZOJ2555 SubString
@(XSY)[LCT]
Description
懒得写背景了,给你一个字符串init,要求你支持两个操作
- 在当前字符串的后面插入一个字符串
- 询问字符串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
Solution
这题的思路还是非常显而易见的.
由于要求在线处理, 我们对字符串用后缀自动机处理, 建立出parent tree.
根据parent tree的性质, 一个父亲节点所表示的所有substrings的出现位置相同, 并且为其所有子节点的substrings的出现位置的集合的并. 所以我们可以用link-cut-tree维护这一棵parent tree. 在插入一个字符时, 对其所对应的parent tree上到根的路径上的节点全部加一. 查询时直接输出一个点上的值即可.
link-cut-tree太久没有写过了, 非常不熟练. 代码中标注了一些写的时候容易出错的地方.
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
const int LEN = 1 << 20, ORG = 'A', LIM = 1 << 5;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1;
while(isdigit(c))
a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
inline int getString(char *str)
{
char c;
while(! isgraph(c = getchar()));
int len = 0;
while(isgraph(c))
str[len ++] = c, c = getchar();
return len;
}
}
struct linkCutTree
{
struct node
{
node *pre, *suc[2];
int w, tag, isRoot;
inline node()
{
pre = suc[0] = suc[1] = NULL;
w = tag = 0;
isRoot = 1;
}
inline void add(int a)
{
if(this == NULL)
return;
tag += a, w += a;
}
inline void pushdown()
{
if(! isRoot)
pre->pushdown();
suc[0]->add(tag), suc[1]->add(tag);
tag = 0;
}
inline int getRelation()
{
if(pre == NULL)
return -1;
return this == pre->suc[1];
}
};
inline void rotate(node *u)
{
node *pre = u->pre, *prepre = pre->pre;
int k = u->getRelation();
if(u->suc[k ^ 1] != NULL)
u->suc[k ^ 1]->pre = pre;
pre->suc[k] = u->suc[k ^ 1];
u->suc[k ^ 1] = pre;
u->pre = prepre;
if(! pre->isRoot) //Link-cut-tree的Splay不要写错
prepre->suc[pre->getRelation()] = u;
else
u->isRoot = 1, pre->isRoot = 0;
pre->pre = u;
}
inline void splay(node *u)
{
u->pushdown();
while(! u->isRoot)
{
if(! u->pre->isRoot)
rotate(u->getRelation() == u->pre->getRelation() ? u->pre : u);
rotate(u);
}
}
inline void access(node *u)
{
splay(u);
while(u->pre != NULL)
{
node *pre = u->pre;
splay(pre);
if(pre->suc[1] != NULL)
pre->suc[1]->isRoot = 1;
u->isRoot = 0;
pre->suc[1] = u;
splay(u);
}
}
inline void cut(node *u)
{
access(u);
if(u->suc[0] != NULL)
u->suc[0]->pre = NULL, u->suc[0]->isRoot = 1;
u->suc[0] = NULL;
}
inline void link(node *u, node *pre)
{
cut(u);
u->pre = pre;
}
}tr;
struct suffixAutomaton
{
struct state
{
int stp;
linkCutTree::node *treeNode;
state *pre, *suc[LIM];
inline state(int _stp = 0)
{
stp = _stp;
treeNode = new linkCutTree::node;
pre = NULL;
memset(suc, NULL, sizeof(suc));
}
};
state *s, *lst;
inline void init()
{
s = lst = new state(); //初始化时新建源点
}
inline void add(int c)
{
state *p = lst, *u = new state(p->stp + 1);
for(; p != NULL && p->suc[c] == NULL; p = p->pre)
p->suc[c] = u;
if(p == NULL)
u->pre = s, tr.link(u->treeNode, s->treeNode);
else
{
state *q = p->suc[c];
if(q->stp == p->stp + 1)
u->pre = q, tr.link(u->treeNode, q->treeNode);
else
{
state *v = new state(p->stp + 1);
v->pre = q->pre;
memcpy(v->suc, q->suc, sizeof(q->suc));
tr.link(v->treeNode, q->pre->treeNode);
q->pre = u->pre = v;
tr.link(q->treeNode, v->treeNode), tr.link(u->treeNode, v->treeNode);
v->treeNode->w = q->treeNode->w;
for(; p != NULL && p->suc[c] == q; p = p->pre)
p->suc[c] = v;
}
}
lst = u;
tr.access(u->treeNode);
u->treeNode->add(1);
}
inline void insert(char *str, int len)
{
for(int i = 0; i < len; ++ i) //插入时不需要新建源点
add(str[i] - ORG);
}
inline int query(char *str, int len)
{
state *p = s;
int i;
for(i = 0; p != NULL && i < len; ++ i)
p = p->suc[str[i] - 'A'];
if(p != NULL)
{
p->treeNode->pushdown();
return p->treeNode->w;
}
return 0;
}
}org;
inline void decodeWithMask(char *str, int len, int mask)
{
for(int i = 0; i < len; ++ i)
{
mask = (mask * 131 + i) % len;
std::swap(str[i], str[mask]);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj2555.in", "r", stdin);
freopen("bzoj2555.out", "w", stdout);
#endif
using namespace Zeonfai;
int q = getInt();
static char str[LEN];
int len = getString(str);
org.init();
org.insert(str, len);
int mask = 0;
while(q --)
{
char opt[1 << 4];
getString(opt);
int len = getString(str);
decodeWithMask(str, len, mask);
if(opt[0] == 'A')
org.insert(str, len);
else
{
int ans = org.query(str, len);
mask ^= ans;
printf("%d\n", ans);
}
}
}