大佬博客
题目链接
题意:刚开始给定n个字符串,每一个字符串有一个权值,后面有q次询问,1代表将a字符串的权值改为吧,2为询问有多少个字符串的后缀包含a并且权值是小于等于a字符串的权值的。
思路:上面大佬的博客里面讲了字符串的hash,这道题预处理处每一个字符串的hash值,然后n^2统计出任意两个字符串之间的后缀关系,最后用nq的复杂度查找就好了。
hash值储存在unsigned long long里面, 那样溢出时,会自动取余2的64次方
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL unsigned long long
using namespace std;
const int maxn=1e6+10;
struct zp
{
char s[1010];
int w,len;
} node[1010];
LL p=131//取奇数质数 31 131;
LL hash[1010][1010];//存每一个字符串所有前缀的hash值
LL ha[maxn];
void init(int n)//预处理每一个字符串的hash值
{
ha[0]=1;
for(int i=1; i<=1010; i++)
ha[i]=ha[i-1]*p;
for(int i=1; i<=n; i++)
{
hash[i][0]=0;
for(int j=1; j<=node[i].len; j++)
hash[i][j]=hash[i][j-1]*p+node[i].s[j-1];
}
}
LL get_hash(int l,int r,int d)//得到d字符串l~r区间的hash值
{
return hash[d][r]-hash[d][l-1]*ha[r-l+1];
}
vector<int> v[1010];
int main()
{
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%s%d",node[i].s,&node[i].w);
v[i].clear();
node[i].len=strlen(node[i].s);
}
init(n);
for(int i=1;i<=n;i++)//n^2处理任意两个字符串之间的后缀关系
{
v[i].push_back(i);//v[i].push_back(j);代表第i个字符串是第j个字符串的后缀
for(int j=i+1;j<=n;j++)
{
if(node[i].len==node[j].len)
{
if(get_hash(1,node[i].len,i)==get_hash(1,node[j].len,j))
v[i].push_back(j),v[j].push_back(i);
}
else if(node[i].len>node[j].len)
{
if(get_hash(node[i].len-node[j].len+1,node[i].len,i)==get_hash(1,node[j].len,j))
v[j].push_back(i);
}
else if(node[i].len<node[j].len)
{
if(get_hash(1,node[i].len,i)==get_hash(node[j].len-node[i].len+1,node[j].len,j))
v[i].push_back(j);
}
}
}
int q;
scanf("%d",&q);
while(q--)//nq查询
{
int opt;
scanf("%d",&opt);
if(opt==1)
{
int a,b;
scanf("%d%d",&a,&b);
node[a].w=b;
}
else if(opt==2)
{
int a;
scanf("%d",&a);
int len=v[a].size(),ans=0;
for(int i=0;i<len;i++)
if(node[v[a][i]].w<=node[a].w)
ans++;
printf("%d\n",ans);
}
}
}
}