2021山东省赛f.Birthday Cake(双哈希考虑贡献)

题目大意:给n个字符串,求有多少对字符串可以组成平方串(可以分为两个一样的子串)

思路:考虑贡献,哈希/kmp

 对于一个字符串,有两种情况,一种是出现公共前后缀,然后接上另一个字符串(绿色),我们枚举公共前后缀的长度(黄色部分,哈希/kmp实现),然后考虑黑色部分的贡献,如果之前黑色部分出现过cnt次,那本次黑色的贡献则为cnt,以此类推。字符串的查询和存储我们用哈希实现。

情况二,对于整个字符串(绿色字符串),我们不仅要加上之前出现过和他一样的字符串的出现次数(也可以理解为情况一时公共前后缀为0),还要计算上它作为其他串的中间部分的贡献(和上面字符串组成平方串)。

注意:1,此题需要双哈希才能过,用pair存舒服一点

2,区别回文和公共前后缀,最后一个for循环里,如果当前不是公共前后缀那么也不能break,可能后面还会有,和回文串不一样的性质!!(调了一晚上)

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
using namespace std;
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pb push_back
#define IOS ios::sync_with_stdio(false)
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define all(v) v.begin(),v.end()
typedef long long ll;
const int N=4e5+10;
const int mod1 = 1e9+7;
const int mod2 = 998244353;
map<pair<int ,int > ,int > mp1,mp2;//整个串和内部
pair <int,int > pi;
int p=101;
int n;
int hash1[N];
int hash2[N];
ll qsm(int a, int b ,int mod)
{
    ll ans=1,temp=a;
    while( b )
    {
        if( b&1) ans = (ans * temp ) %mod;
        temp = ( temp * temp)%mod;
        b>>=1;
    }
    return ans%mod;
}
pair<int,int> get(int l,int r)
{
    int x= (hash1[r]+mod1 - hash1[l-1]*qsm(p,r-l+1,mod1)%mod1)%mod1;
    int y= (hash2[r]+mod2 - hash2[l-1]*qsm(p,r-l+1,mod2)%mod2)%mod2;
    return make_pair(x,y);
}
signed main()
{
    //  /!!!
//        freopen("data.txt","r",stdin);
    //    !!!
//    IOS;
    cin>>n;
    ll ans = 0;
    _for(i,1,n){
        string s;cin>>s;
        int len = s.size();
        for(int i=1;i<=len;++i){
            hash1[i] = (hash1[i-1]*p+s[i-1]-'a'+1)%mod1;
            hash2[i] = (hash2[i-1]*p+s[i-1]-'a'+1)%mod2;
        }
        pair<ll,ll> temp = make_pair(hash1[len],hash2[len]);
        ans += mp1[temp]; // 加上跟该串一模一样的
        mp1[temp]++;
        ans += mp2[temp]; // 加上内部和{该串整串}一样的串
        for(int i=1;2*i<len;++i){
            pair<int,int> x= get(1,i);
            pair<int,int> y= get(len-i+1,len);
            if(x==y){
                // 前后缀一样
                pair<int,int> z = get(i+1,len-i);
                // 中间部分
                ans += mp1[z]; // 加上整串和该串内部一样的串
                mp2[z]++;
            }
        }
    }
    cout<<ans;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值