2011-0050
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
给一个初始长度小于1e5的字符串,有4个操作或询问:
1:Reverse a b: 把a到b的字符反转
2:Modify p c : 把p字符修改为c
3:Lcp a b : 求第a个字符开始的后缀,与第b个字符开始的后缀的最长公共前缀的长度
4:Palindrome :求整个字符串的最长回文子串(此询问不超过10次)
首先要支持反转操作和修改操作,可以用块状链表或Spaly。
以Splay为例,每个节点除了常规的lchild指针,rchild指针,parent指针(如果写自定向下Splay可以省略),size域,
还需要储存反转标记,为了求Lcp,还要有以某节点为根的树对应的字符串的hash值,同时兼顾反转操作,还要有对应反向字符串的hash值
这样在Splay树的向下查找时,下传反转标记,对换hash值(如果有反转标记)。求Lcp可以二分长度,比较hash值是否相同。这样求Lcp复杂度就是O(log n × log n)
,修改O(log n);反转也是 O(log n)。
至于回文字串,可以取出所有字符,用O(n)的Manacher算法,因为次数不多,可以接受。
{{{
#define ULL unsigned long long
#define HashMOD 2147483647l
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef char ktype;
struct SplayNode{
SplayNode *l,*r,*p;
ktype k;
unsigned sz,hash,rhash;bool rev;
int key(){return rev?rhash: hash;}
int rkey(){return rev? hash:rhash;}
};
typedef SplayNode* node;
SplayNode NILnode;
const node NIL = &NILnode;
const int MaxOnline = 100000+ 20 ;
SplayNode T[MaxOnline];
unsigned PN[MaxOnline];//for hash
node memptr;
struct Splay{
node sroot;
static void Initialize(){//once and only once before all
NIL->sz=NIL->hash=NIL->rhash=0;
NIL->l=NIL->r=NIL->p= &*NIL;
NIL->rev= false;
memptr= &T[0];
PN[0]=1;ULL t=1;
for(int i= 1; i< MaxOnline;++i){
T[i-1].r = T + i;
t*=27;if(t>HashMOD)t%=HashMOD;
PN[i] =unsigned(t);
}
T[MaxOnline-1].r = NIL;
}
node newnode(const node &p,const ktype& kk){
node tmp=memptr;memptr=memptr->r;
if(tmp!=NIL){
tmp->p = p;
tmp->k = kk;
tmp->hash = tmp ->rhash = kk;
tmp->l=tmp->r=NIL;
tmp->sz=1;
tmp->rev= false;
return tmp;
}else perror("OUT OF MEMORY");
}
void freenode(const node& xx){if(xx!=NIL){xx->r=memptr;memptr=xx;}}
void freewhole(const node& xx){
if(xx->l!=NIL)freewhole(xx->l);
if(xx->r!=NIL)freewhole(xx->r);
freenode(xx);
}
Splay(){sroot=NIL;}
~Splay(){ if(sroot->sz>5000)shrink(sroot,8);freewhole(sroot);}
void clear(){if(sroot->sz>5000)shrink(sroot,8);freewhole(sroot);sroot=NIL;}
void revnode(const node& x){
if(x!=NIL)x->rev=!x->rev;
}
void pushdown(const node& x){
if(x->rev){
x->rev=false;
revnode(x->l);
revnode(x->r);
std::swap(x->l,x->r);
std::swap(x->hash,x->rhash);
}
}
void update(const node& x){
pushdown(x);
int lsz =x->l->sz, rsz =x->r->sz;
x->sz= lsz + rsz +1;
x-> hash =(unsigned )( (x->l-> key() * (ULL)PN[rsz+1] + x->k *(ULL)PN[rsz] + x->r-> key())% HashMOD);
x->rhash =(unsigned )( (x->r->rkey() * (ULL)PN[lsz+1] + x->k *(ULL)PN[lsz] + x->l->rkey())% HashMOD);
}
void zig(node x){
node y=x->p;
y->l=x->r;if(x->r!=NIL)x->r->p=y;
x->p=y->p;
if(x->p!=NIL){
if(x->p->l==y)x->p->l = x;
else x->p ->r=x;
}x->r=y;y->p=x;
update(y);
update(x);
}
void zag(node y){
node x = y->p;
x->r=y->l;if(y->l!=NIL)y->l->p=x;
y->p=x->p;
if(y->p!=NIL){
if(y->p->l==x)y->p->l = y;
else y->p ->r=y;
}y->l=x;x->p=y;
update(x);
update(y);
}
void splay(node x,node rt){
node xp;
while(x->p!=rt){
xp=x->p;
if(xp->p == rt){
if(x == xp->l)zig(x);
else zag(x);
}else if(xp->p->l== xp){
if(xp->l==x){ //LL
zig(xp);zig(x);
}else{ //LR
zag(x);zig(x);
}
}else{
if(xp->r==x){ // RR
zag(xp);zag(x);
}else{ //RL
zig(x);zag(x);
}
}
}
if(rt ==NIL)sroot=x;
}
node rank(int k,node t){//start from 1 2 3 .. in Splay node t
//assert(k<=t->sz);
node tr= t->p;
while(k){
pushdown(t);
if(k<=t->l->sz)
t=t->l;
else{
k-=t->l->sz;
if(--k)t=t->r;
else break;
}
}
splay(t,tr);
return t;
}
void insert(int pos,const ktype& kk){// after insert, it's in pos+1 in squence
if(sroot==NIL){sroot= newnode(NIL,kk);return;}
node t= sroot;
while(pos || t->l!=NIL){
pushdown(t);
t->sz++;//
if(pos<=t->l->sz)t=t->l;
else{
pos-=t->l->sz+1;
if(t->r!=NIL)t=t->r;
else{
t->r = newnode(t,kk);
splay(t->r,NIL);
return;
}
}
}
t->l=newnode(t,kk);
splay(t->l,NIL);
}
void modify(int pos,const ktype& kk){
rank(pos,sroot);
sroot->k = kk;
update(sroot);
}
node gather(int l,int r){//[l,r] l<=r;
if(l>r)return NIL;
if(l==1){
if(r==sroot->sz)return sroot;
else return rank(r+1,sroot)->l;
}else
if(r==sroot ->sz)return rank(l-1,sroot)->r;
else{
rank(l-1,sroot);
return rank(r-l+2,sroot->r)->l;
}
}
node join(node x,node y){
x->p=NIL;y->p=NIL;
if(x==NIL)return y;
if(y==NIL)return x;
pushdown(x);
while(x->r!=NIL){x=x->r;pushdown(x);}
splay(x,NIL);
x->r=y;
y->p=x;update(x);
return x;
}
void reverse(int l,int r){
node t=gather(l,r);if(l<r)revnode(t);pushdown(t);
splay(t,NIL);
}
void erase(int l,int r){
node t=gather(l,r);
freewhole(t);
splay(t->p,NIL);
}
void erase(int k){
rank(k,sroot);
node tmp= sroot;
sroot =join(sroot ->l,sroot->r);
freenode(tmp);
}
void shrink(node x,int h){
if(x->sz<8 || !h)return;
x=rank((x->sz+1)>>1,x);
shrink(x->l,h-1);
shrink(x->r,h-1);
}
void getwhole(node x,ktype* &p){pushdown(x);
if(x->sz>100)x=rank((x->sz+1)>>1,x);
if(x->l!=NIL)getwhole(x->l,p);
*p++= x->k /*+96*/;
*p++='#';
if(x->r!=NIL)getwhole(x->r,p);
}
};
#include<cstring>
#include<cctype>
#include<algorithm>
int getnum(){
static char c;
while(!isdigit(c=getchar()));
int t=0;
do t=t*10+c-48;while(isdigit(c=getchar()));
return t;
}
char s[MaxOnline*2],c,*p,*q;
int F[MaxOnline*2];
int M,n,i,l,r,m,a,b;
Splay sp;
int main(){
Splay::Initialize();
while(gets(s),scanf("%d",&M)==1){
getchar();
n=strlen(s);
sp.clear();
for(i=0;i<n;++i)sp.insert(i,s[i] - 96);
while(M--){
switch(getchar()){
case 'R': l= getnum();r=getnum();
sp.reverse(l,r);
break;
case 'M':a=getnum();do c= getchar();while(isspace(c));
sp.modify(a,c-96);
getchar();//skip \n
break;
case 'L':
a = getnum();
b = getnum();
l =0; r=std::min(n-a,n-b)+2;
while(l+1<r){
m = (l+r)>>1;
node t = sp.gather(a,a+m-1);
unsigned h1=t->key();
unsigned h2=t->rkey();
t= sp.gather(b,b+m-1);
if(h1==t->key()&&h2==t->rkey())l= m;
else r= m;
}
printf("%d\n",l);
break;
case 'P':
s[0]='!';s[1]='#';p=s+2;
sp.getwhole(sp.sroot,p);
*p =0;//puts(s);
F[0]=1;
r=1;m=0;l=n*2+1; a=0;
for(int i=1;i<=l;++i){
int &fi=F[i];
if(r>i)fi=std::min(F[m*2-i],r-i);else fi=1;
p=s+i-fi;q=s+i+fi;
for(;*p-- ==*q++;++fi);
if(i+fi>r){r=i+fi;m=i;}
if(a<fi)a=fi;//b=i;}
}
printf("%d\n",a-1);
while(getchar()!='\n');
break;
}
}
}
return 0;
}
}}}
[by ZhouYuChen]
给一个初始长度小于1e5的字符串,有4个操作或询问:
1:Reverse a b: 把a到b的字符反转
2:Modify p c : 把p字符修改为c
3:Lcp a b : 求第a个字符开始的后缀,与第b个字符开始的后缀的最长公共前缀的长度
4:Palindrome :求整个字符串的最长回文子串(此询问不超过10次)
首先要支持反转操作和修改操作,可以用块状链表或Spaly。
以Splay为例,每个节点除了常规的lchild指针,rchild指针,parent指针(如果写自定向下Splay可以省略),size域,
还需要储存反转标记,为了求Lcp,还要有以某节点为根的树对应的字符串的hash值,同时兼顾反转操作,还要有对应反向字符串的hash值
这样在Splay树的向下查找时,下传反转标记,对换hash值(如果有反转标记)。求Lcp可以二分长度,比较hash值是否相同。这样求Lcp复杂度就是O(log n × log n)
,修改O(log n);反转也是 O(log n)。
至于回文字串,可以取出所有字符,用O(n)的Manacher算法,因为次数不多,可以接受。
#define ULL unsigned long long
#define HashMOD 2147483647l
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef char ktype;
struct SplayNode{
SplayNode *l,*r,*p;
ktype k;
unsigned sz,hash,rhash;bool rev;
int key(){return rev?rhash: hash;}
int rkey(){return rev? hash:rhash;}
};
typedef SplayNode* node;
SplayNode NILnode;
const node NIL = &NILnode;
const int MaxOnline = 100000+ 20 ;
SplayNode T[MaxOnline];
unsigned PN[MaxOnline];//for hash
node memptr;
struct Splay{
node sroot;
static void Initialize(){//once and only once before all
NIL->sz=NIL->hash=NIL->rhash=0;
NIL->l=NIL->r=NIL->p= &*NIL;
NIL->rev= false;
memptr= &T[0];
PN[0]=1;ULL t=1;
for(int i= 1; i< MaxOnline;++i){
T[i-1].r = T + i;
t*=27;if(t>HashMOD)t%=HashMOD;
PN[i] =unsigned(t);
}
T[MaxOnline-1].r = NIL;
}
node newnode(const node &p,const ktype& kk){
node tmp=memptr;memptr=memptr->r;
if(tmp!=NIL){
tmp->p = p;
tmp->k = kk;
tmp->hash = tmp ->rhash = kk;
tmp->l=tmp->r=NIL;
tmp->sz=1;
tmp->rev= false;
return tmp;
}else perror("OUT OF MEMORY");
}
void freenode(const node& xx){if(xx!=NIL){xx->r=memptr;memptr=xx;}}
void freewhole(const node& xx){
if(xx->l!=NIL)freewhole(xx->l);
if(xx->r!=NIL)freewhole(xx->r);
freenode(xx);
}
Splay(){sroot=NIL;}
~Splay(){ if(sroot->sz>5000)shrink(sroot,8);freewhole(sroot);}
void clear(){if(sroot->sz>5000)shrink(sroot,8);freewhole(sroot);sroot=NIL;}
void revnode(const node& x){
if(x!=NIL)x->rev=!x->rev;
}
void pushdown(const node& x){
if(x->rev){
x->rev=false;
revnode(x->l);
revnode(x->r);
std::swap(x->l,x->r);
std::swap(x->hash,x->rhash);
}
}
void update(const node& x){
pushdown(x);
int lsz =x->l->sz, rsz =x->r->sz;
x->sz= lsz + rsz +1;
x-> hash =(unsigned )( (x->l-> key() * (ULL)PN[rsz+1] + x->k *(ULL)PN[rsz] + x->r-> key())% HashMOD);
x->rhash =(unsigned )( (x->r->rkey() * (ULL)PN[lsz+1] + x->k *(ULL)PN[lsz] + x->l->rkey())% HashMOD);
}
void zig(node x){
node y=x->p;
y->l=x->r;if(x->r!=NIL)x->r->p=y;
x->p=y->p;
if(x->p!=NIL){
if(x->p->l==y)x->p->l = x;
else x->p ->r=x;
}x->r=y;y->p=x;
update(y);
update(x);
}
void zag(node y){
node x = y->p;
x->r=y->l;if(y->l!=NIL)y->l->p=x;
y->p=x->p;
if(y->p!=NIL){
if(y->p->l==x)y->p->l = y;
else y->p ->r=y;
}y->l=x;x->p=y;
update(x);
update(y);
}
void splay(node x,node rt){
node xp;
while(x->p!=rt){
xp=x->p;
if(xp->p == rt){
if(x == xp->l)zig(x);
else zag(x);
}else if(xp->p->l== xp){
if(xp->l==x){ //LL
zig(xp);zig(x);
}else{ //LR
zag(x);zig(x);
}
}else{
if(xp->r==x){ // RR
zag(xp);zag(x);
}else{ //RL
zig(x);zag(x);
}
}
}
if(rt ==NIL)sroot=x;
}
node rank(int k,node t){//start from 1 2 3 .. in Splay node t
//assert(k<=t->sz);
node tr= t->p;
while(k){
pushdown(t);
if(k<=t->l->sz)
t=t->l;
else{
k-=t->l->sz;
if(--k)t=t->r;
else break;
}
}
splay(t,tr);
return t;
}
void insert(int pos,const ktype& kk){// after insert, it's in pos+1 in squence
if(sroot==NIL){sroot= newnode(NIL,kk);return;}
node t= sroot;
while(pos || t->l!=NIL){
pushdown(t);
t->sz++;//
if(pos<=t->l->sz)t=t->l;
else{
pos-=t->l->sz+1;
if(t->r!=NIL)t=t->r;
else{
t->r = newnode(t,kk);
splay(t->r,NIL);
return;
}
}
}
t->l=newnode(t,kk);
splay(t->l,NIL);
}
void modify(int pos,const ktype& kk){
rank(pos,sroot);
sroot->k = kk;
update(sroot);
}
node gather(int l,int r){//[l,r] l<=r;
if(l>r)return NIL;
if(l==1){
if(r==sroot->sz)return sroot;
else return rank(r+1,sroot)->l;
}else
if(r==sroot ->sz)return rank(l-1,sroot)->r;
else{
rank(l-1,sroot);
return rank(r-l+2,sroot->r)->l;
}
}
node join(node x,node y){
x->p=NIL;y->p=NIL;
if(x==NIL)return y;
if(y==NIL)return x;
pushdown(x);
while(x->r!=NIL){x=x->r;pushdown(x);}
splay(x,NIL);
x->r=y;
y->p=x;update(x);
return x;
}
void reverse(int l,int r){
node t=gather(l,r);if(l<r)revnode(t);pushdown(t);
splay(t,NIL);
}
void erase(int l,int r){
node t=gather(l,r);
freewhole(t);
splay(t->p,NIL);
}
void erase(int k){
rank(k,sroot);
node tmp= sroot;
sroot =join(sroot ->l,sroot->r);
freenode(tmp);
}
void shrink(node x,int h){
if(x->sz<8 || !h)return;
x=rank((x->sz+1)>>1,x);
shrink(x->l,h-1);
shrink(x->r,h-1);
}
void getwhole(node x,ktype* &p){pushdown(x);
if(x->sz>100)x=rank((x->sz+1)>>1,x);
if(x->l!=NIL)getwhole(x->l,p);
*p++= x->k /*+96*/;
*p++='#';
if(x->r!=NIL)getwhole(x->r,p);
}
};
#include<cstring>
#include<cctype>
#include<algorithm>
int getnum(){
static char c;
while(!isdigit(c=getchar()));
int t=0;
do t=t*10+c-48;while(isdigit(c=getchar()));
return t;
}
char s[MaxOnline*2],c,*p,*q;
int F[MaxOnline*2];
int M,n,i,l,r,m,a,b;
Splay sp;
int main(){
Splay::Initialize();
while(gets(s),scanf("%d",&M)==1){
getchar();
n=strlen(s);
sp.clear();
for(i=0;i<n;++i)sp.insert(i,s[i] - 96);
while(M--){
switch(getchar()){
case 'R': l= getnum();r=getnum();
sp.reverse(l,r);
break;
case 'M':a=getnum();do c= getchar();while(isspace(c));
sp.modify(a,c-96);
getchar();//skip \n
break;
case 'L':
a = getnum();
b = getnum();
l =0; r=std::min(n-a,n-b)+2;
while(l+1<r){
m = (l+r)>>1;
node t = sp.gather(a,a+m-1);
unsigned h1=t->key();
unsigned h2=t->rkey();
t= sp.gather(b,b+m-1);
if(h1==t->key()&&h2==t->rkey())l= m;
else r= m;
}
printf("%d\n",l);
break;
case 'P':
s[0]='!';s[1]='#';p=s+2;
sp.getwhole(sp.sroot,p);
*p =0;//puts(s);
F[0]=1;
r=1;m=0;l=n*2+1; a=0;
for(int i=1;i<=l;++i){
int &fi=F[i];
if(r>i)fi=std::min(F[m*2-i],r-i);else fi=1;
p=s+i-fi;q=s+i+fi;
for(;*p-- ==*q++;++fi);
if(i+fi>r){r=i+fi;m=i;}
if(a<fi)a=fi;//b=i;}
}
printf("%d\n",a-1);
while(getchar()!='\n');
break;
}
}
}
return 0;
}
[by ZhouYuChen]