Flying2018
2020-01-04 12:54:17
以下默认字符串下标从1开始,用
字符串 的部分分中。
字符串
由于单纯的字符串
字符串
其中
由于
比如如果令
那么
可以发现,我们本质上是将
那为什么要采用这样一个
参照哈希表,我们知道如果两个字符串的
但事实上我们常常需要截取一个字符串的子串。可以发现,对于
考虑原串
于是可以推出:
即
于是对于原串记录
由于C++的
#define ull unsigned long long
好了,你已经学会了字符串 所有技巧,让我们来试试吧(雾
字符串
接下来先介绍字符串
这个其实不能说是骗分,毕竟枚举起始点扫一遍
代码略。
考虑以同一个字符为中心的回文串的子串一定是回文串,所以满足可二分性。
将字符串正着和倒着
时间复杂度相比较 发现过不了模板题。
关键代码
ull num[22000000],num2[22000010];
ull find_hash(int l,int r)
{
if(l<=r)
return num[r]-num[l-1]*_base[r-l+1];//顺序Hash
return num2[r]-num2[l+1]*_base[l-r+1];//倒序Hash
}
int l=0,r=min(i-1,len-i);
int len=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(find_hash(i,i+mid)==find_hash(i,i-mid)) l=mid+1,len=mid;//要求顺序Hash与倒序Hash匹配
else r=mid-1;
}
如果存在长度为
考虑筛出每个数的最大质因数,
char str[N];
int len;
ull hashs[N],bases[N];
void make_hash(void)
{
bases[0]=1;
for(int i=1;i<=len;i++)
{
hashs[i]=hashs[i-1]*base+str[i]-'a'+1;
bases[i]=bases[i-1]*base;
}
}//预处理Hash值
ull get_hash(int l,int r){return hashs[r]-hashs[l-1]*bases[r-l+1];}
int prime[N],nxt[N],cnt;
int num[N],tot;
int main()
{
scanf("%d",&len);
scanf("%s",str+1);
make_hash();
for(int i=2;i<=len;i++)
{
if(!nxt[i]) nxt[i]=prime[++cnt]=i;
for(int j=1;j<=cnt && i*prime[j]<=len;j++)
{
nxt[i*prime[j]]=prime[j];//筛出每个数的最大质因数
if(i%prime[j]==0) break;
}
}
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
int lens=r-l+1;
int ans=0;
tot=0;
while(lens>1)
{
num[++tot]=nxt[lens];
lens/=nxt[lens];//质因数分解
}
lens=r-l+1;
for(int j=1;j<=tot;j++)
{
int len1=lens/num[j];//进行试除
if(get_hash(l,r-len1)==get_hash(l+len1,r)) lens=len1;//试除成立就取试除后的结果
}
printf("%d\n",lens);
}
return 0;
}
现在的大多数字符串算法都是静态的查询或者只允许在末尾/开头添加。但如果要求在字符串中间插入或修改,很多算法就无能为力了。
而字符串
一般来说,修改操作中带 插入,可持久化,区间修改 这类字眼的字符串题大多就是 或是真·毒瘤题。
题意:求两个后缀的
用平衡树维护区间的
关键代码。
inline void update(int u)
{
siz[u]=siz[ch[u][0]]+siz[ch[u][1]]+1;
sum[u]=sum[ch[u][0]]*bases[siz[ch[u][1]]+1]+val[u]*bases[siz[ch[u][1]]]+sum[ch[u][1]];//合并Hash值
}
/*
此处插入你喜欢的平衡树
*/
int main()
{
srand(19260817);
scanf("%s",str+1);
int m;
scanf("%d",&m);
n=strlen(str+1);
bases[0]=1;
for(int i=1;i<=100000;i++) bases[i]=bases[i-1]*base;
for(int i=1;i<=n;i++) root=t.merge(root,t.new_node(str[i]-'a'+1));//平衡树的初始化
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%s%d",opt,&x);
if(opt[0]=='Q')
{
scanf("%d",&y);
printf("%d\n",lcp(x,y));//查询
}
else if(opt[0]=='R')
{
scanf("%s",opt);
t.erase(root,x);
t.insert(root,x-1,opt[0]-'a'+1);//往平衡树中删除
}
else if(opt[0]=='I')
{
scanf("%s",opt);
t.insert(root,x,opt[0]-'a'+1);//往平衡树中插入
n++;
}
}
return 0;
}
题目大意:求最短路,其中每条边边权为
假如不考虑时间复杂度,可以直接高精度加最短路得到
我们发现这个
对于后者,我们发现这个可以看成是一个字符串求字典序大小的题。而线段树又恰好可以支持这样的
考虑
还有,由于直接复制显然会T,这里的线段树需要可持久化,就是每个节点继承转移节点(最短路树上的父亲)的信息同时加上一次修改。
时间复杂度
关键代码:
void update(int u,int l,int r)
{
int mid=(l+r)>>1;
hs[u]=hs[ls[u]]+hs[rs[u]]*bs[mid-l+1];//合并Hash值
val[u]=val[ls[u]]==(mid-l+1)?val[ls[u]]+val[rs[u]]:val[ls[u]];//从l开始1的个数
}
int cmp(int u,int v)//比较u,v两树的大小
{
int fu=u,fv=v;
int l=0,r=n;
while(1)
{
if(!u || tag[u]) return fv;//如果有一方全是0,直接跳出
if(!v || tag[v]) return fu;
if(l==r) return hs[u]<=hs[v]?fv:fu;//如果到了叶节点,直接比较
int mid=(l+r)>>1;
if(hs[rs[u]]==hs[rs[v]]) u=ls[u],v=ls[v],r=mid;//如果右子树相同,就比较左子树
else u=rs[u],v=rs[v],l=mid+1;//否则比较右子树
}
}
完整代码详见我的博客。
题意:维护一个字符串的子串的集合,一开始字符串和集合均为空。
要求完成:在集合中插入一个子串,在字符串末尾加一个字符,求集合中与当前询问的子串
比如字符串为
集合中的子串为
此时查询与子串
显然上述的方法都不可行。
考虑使用
仿照这个思路,使用上述比较两个串字典序大小的方法,考虑使用平衡树来维护子串集合中字典序的顺序,查询时只需查询前驱后继中的
时间复杂度
题目都没有,代码肯定咕了
由此可以发现:凡是维护区间,支持维护区间合并(好像区间 在线带修莫队,大都可以套上
upd:今年ZJOI2020喜闻乐见地出了一道可以用
由于上述两题也是利用其实是我太菜了,以上两题都没有过。)
如果以上题目不能满足你的需求,可以参考这份题单
虽然字符串
事实上,自然溢出的
题意:卡单
upd: bzoj没了,题目也不见了。
根据生日悖论,对于
通过代入求值可以发现,对于该题,尽管
放一张度娘的图(图中的
通过式子可以发现,如果想要
这时候就要用到双
同样,如果两个串第一个
所以假如用自然溢出可能会被卡的情况下(其实一般情况下是不会的),建议写双
我常用的双
#define ll long long
#define P pair<ll,ll>
#define MP make_pair
#define fi first
#define se second
#define mod 1000000007
P operator +(const P a,const P b){return MP((a.fi+b.fi)%mod,(a.se+b.se)%mod);}
P operator -(const P a,const P b){return MP((a.fi-b.fi+mod)%mod,(a.se-b.se+mod)%mod);}
P operator *(const P a,const P b){return MP(a.fi*b.fi%mod,a.se*b.se%mod);}
const P base=MP(233,2333);
//后续代码同单Hash
不过,双
当然,在codeforces上写自然溢出的绝对是勇士(别问我怎么知道的。
虽然字符串
但是字符串 骗分方式。
当然,大多数情况下 所以就算会了Hash我还是咸鱼。