给定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序。