字符串hash

大佬博客
题目链接
题意:刚开始给定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);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值