给定n个字符串,每个字符串一个价值wi,询问
1 更改某个串的价值
2 查询以第x个串为后缀且价值小等于x串的字符串个数
字符串个数 n<=1000
查询数 q<=80000
分析:根据字符串倒序构建字典树,那么对于某个字符串,以它为后缀的字符串在该字典树中成为了它的子树。然后根据字典树构建dfs序,查询相当于dfs序的区间查询。
算法一: 使用hash预处理出树,然后构建dfs序,分块进行查询和更新。
hash预处理n^2,查询sqrt(n),更新sqrt(n),总时间复杂度:O(q*sqrt(n))
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define pri 233
#define M 1000000007
typedef long long ll;
using namespace std;
struct BlockQuery{
int num[35][35][35];
int block,cnt;
int digit[1010];
BlockQuery(int n){
int i=1;
for (;i*i<n;i++);
block=i;
cnt=(n-1)/block+1;
memset(num,0,sizeof(num));
}
void add(int pos,int val,int f){
int loc=pos/block;
int valsq = val/33;
digit[pos] = val;
for (int i=valsq+1;i<=34;i++)
num[loc][i][34] += f;
int mval = val%33;
for (int i=mval;i<=34;i++)
num[loc][valsq][i] += f;
}
int getLeq(int l,int r,int val){
int lb=l/block,rb=r/block;
int lbr = lb*block+block-1;
int sum=0;
if (lb!=rb){
for (int i=l;i<=lbr;i++)
sum+= (digit[i]<=val)?1:0;
for (int i=rb*block;i<=r;i++)
sum+= (digit[i]<=val)?1:0;
for (int i=lb+1;i<rb;i++){
int valsq = val/33;
if (valsq>0)
sum+= num[i][valsq-1][34];
sum+= num[i][valsq][val%33];
}
}
else
{
for (int i=l;i<=r;i++)
sum += digit[i]<=val?1:0;
}
return sum;
}
};
char st[1010];
int l[1010],r[1010];
struct node{
char st[1010];
int hash[1010];
int len,id,w,color;
} a[1010];
bool cmp0(const node &a,const node &b){
return a.len>b.len;
}
int powt[1010];
int getHashValue(int *h,int l,int r){
return (h[r]-(ll)h[l-1]*powt[r-l+1]%M+M)%M;
}
int tot,Link[1010];
struct node2{
int v,next;
} edge[10100];
void addEdge(int x,int y){
tot++;
edge[tot].v=y;
edge[tot].next=Link[x];
Link[x]=tot;
}
bool vis[1010];
int tim;
int L[1010],R[1010],Loc[1010];
void dfs(int x){
vis[x]=true;
tim++;
Loc[a[x].id]=L[a[x].id]=tim;
for (int p=Link[x];p;p=edge[p].next)
dfs(edge[p].v);
R[a[x].id]=tim;
}
int main(){
powt[0]=1;
for (int i=1;i<1010;i++)
powt[i]=(ll)powt[i-1]*pri%M;
int T;
scanf("%d",&T);
while (T-->0){
int n;
scanf("%d",&n);
BlockQuery bq(n);
for (int i=1;i<=n;i++){
scanf("%s %d",a[i].st+1,&a[i].w);
a[i].id=i;
a[i].len=strlen(a[i].st+1);
a[i].hash[0]=0;
for (int j=1;j<=a[i].len;j++)
a[i].hash[j]=((ll)a[i].hash[j-1]*pri+a[i].st[j])%M;
}
sort(a+1,a+1+n,cmp0);
tot=0;
memset(Link,0,sizeof(Link));
for (int i=1;i<=n;i++){
a[i].color = a[i].id;
for (int j=i+1;j<=n;j++)
{
if (getHashValue(a[j].hash,1,a[j].len)==
getHashValue(a[i].hash,a[i].len-a[j].len+1,a[i].len)){
a[i].color = a[j].id;
addEdge(j,i);
break;
}
}
}
tim=0;
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++)
if (!vis[i]&&a[i].color==a[i].id)
dfs(i);
for (int i=1;i<=n;i++)
for (int j=n;j>i;j--)
if (a[i].len==a[j].len&&a[i].hash[a[i].len]==a[j].hash[a[i].len]){
L[a[i].id] = L[a[j].id];
R[a[i].id] = R[a[j].id];
break;
}
for (int i=1;i<=n;i++)
bq.add(Loc[a[i].id],a[i].w,1);
int q,choose,x,y;
scanf("%d",&q);
for (int i=1;i<=q;i++){
scanf("%d",&choose);
if (choose==1){
scanf("%d%d",&x,&y);
bq.add(Loc[x],bq.digit[Loc[x]],-1);
bq.add(Loc[x],y,1);
}
else{
scanf("%d",&x);
printf("%d\n",bq.getLeq(L[x],R[x],bq.digit[Loc[x]]));
}
}
}
return 0;
}
算法二: 建立字典树,构建dfs序。