2021“MINIEYE杯”中国大学生算法设计超级联赛(8)

目录

Singing Superstar(字符串hash/AC自动机)


Singing Superstar(字符串hash/AC自动机)

题意:

给一长度 < 1e5 的字符串s,q < 1e5次询问,每次问一个长 < 30 的串 t 在s中出现的次数,
且t不可重叠。
例 : “abababa“中“aba“不相交的出现的次数为2。

思路:

一道简单的字符串题,对母串中所有长度小于等于30的串做字符串哈希,对相同哈希值的串暴力统计答
案,每个询问直接查询对应哈希值的答案。
也可以使用Trie/AC自动机来通过本题,数据范围放的很松,理论复杂度稍劣也能通过。

代码:

字符串hash

#include<bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;
typedef long long LL;
typedef pair<int,int> PII;
#define re register
inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'|ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}
int buf[105];
inline void write(int i)
{
    int p = 0;
    if(i == 0) p++;
    else while(i)
        {
            buf[p++] = i % 10;
            i /= 10;
        }
    for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]);
}
const int inf=0x3f3f3f3f;
const double PI = acos(-1);
const int mod=998244353;
int qmi(int m, int k, int p)
{
    int res = 1 % p, t = m;
    while (k)
    {
        if (k&1) res = (LL)res * t % p;
        t = (LL)t * t % p;
        k >>= 1;
    }
    return res;
}
int gcd(int a,int b)
{
    return b ? gcd(b,a%b) : a;
}
const int N=100010;
const int M=N*N;
const ull B=131;
ull h[N],a[N];
ull p[N];
char str[N];
ull get(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",str+1);
        int len=strlen(str+1);
        p[0]=1;
        for(int i=1;i<=len;i++)
        {
            p[i]=p[i-1]*B;
            h[i]=h[i-1]*B+str[i];
        }
        int n;
        scanf("%d",&n);
        map<ull,int>ma;//记录不相交出现了几次
        map<ull,int>last;//记录上一次出现的末尾位置
        for(int i=1;i<=n;i++)
        {
            char s[35];
            scanf("%s",s+1);
            //cout<<s+1<<endl;
            int siz=strlen(s+1);
            ull x=0;
            for(int j=1;j<=siz;j++)
            {
                x=x*B+s[j];
            }
            a[i]=x;
            ma[a[i]]=0;
            last[a[i]]=0;
        }
        for(int i=1;i<=len;i++)
        {
            for(int j=0;j<30&&i+j<=len;j++)
            {
                ull x=get(i,i+j);
                if(ma.count(x)&&last[x]<i)
                {
                    ma[x]++;
                    last[x]=i+j;
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d\n",ma[a[i]]);
        }
    }
    return 0;
}

AC自动机不重叠:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define inff 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define me(a,b) memset(a,b,sizeof(a))
#define min4(a,b,c,d) min(min(a,b),min(c,d))
#define min3(x,y,z) min(min(x,y),min(y,z))
#define max4(a,b,c,d) max(max(a,b),max(c,d))
#define max3(x,y,z) max(max(x,y),max(y,z))
typedef long long ll;
using namespace std;
const int maxn=6e5+5;
const int maxm=1e5+10;
const int N=26;
int ans[maxn];//记录答案
int op[maxn];
int val[maxn];//记录节点末尾位置
struct AC
{
   int fail[maxn];
   int last[maxn];
   int pos[maxn];//记录单词对应的长度
   int ch[maxn][30];
   int cnt,root;
   int newnode()
   {
       for(int i=0;i<N;i++)
          ch[cnt][i]=0;
       pos[cnt]=fail[cnt++]=0;
       return cnt-1;
   }
   void init()
   {
       cnt=0;
       root=newnode();
   }
   void insert(char *s,int x)
   {
       int len=strlen(s);
       int u=0;
       for(int i=0;i<len;i++)
       {
           int v=s[i]-'a';
           if(!ch[u][v])
             ch[u][v]=newnode();
           u=ch[u][v];
           pos[u]=i+1;
       }
       val[x]=u;
   }
   void getfail()
   {
       queue<int >q;
       int u=0;
       for(int i=0;i<N;i++)
        if(ch[u][i])
            q.push(ch[u][i]);
       while(!q.empty())
       {
           u=q.front();
           q.pop();
           for(int i=0;i<N;i++)
           {
               if(ch[u][i])
               {
                   fail[ch[u][i]]=ch[fail[u]][i];
                   q.push(ch[u][i]);
               }
               else
                   ch[u][i]=ch[fail[u]][i];
           }
       }
   }
   void getans(char *s)
   {
       memset(last,-1,sizeof(last));
       memset(ans,0,sizeof(ans));
       int len=strlen(s);
       int u=0;
       for(int i=0;i<len;i++)
       {
           u=ch[u][s[i]-'a'];
           for(int j=u;j;j=fail[j])
           {
                if(i-last[j]>=pos[j])//看看当前位置与之前到达该点的位置差是否大于单词长度
                {
                    ans[j]++;
                    last[j]=i;
                }
           }
       }
   }
}AC;
char str[maxm];
char ss[40];
int main()
{
    int n,cas=0;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",str);
        AC.init();
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",ss);
            AC.insert(ss,i);
        }
        AC.getfail();
        AC.getans(str);
        for(int i=0;i<n;i++)
        printf("%d\n",ans[val[i]]);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值