CF985F Isomorphic Strings (hash)

CF985F Isomorphic Strings

【题意】

定义两个字符串 S 1 , S 2 S1,S2 S1,S2同构当且仅当有一种映射关系,使得 S 1 S1 S1映射后与 S 2 S2 S2相同

然后给出一个字符串,给出Q组询问,每组询问S1字符串中 S 1 [ x ] [ x + l e n − 1 ] S1[x][x+len-1] S1[x][x+len1] S 2 [ y ] [ y + l e n − 1 ] S2[y][y+len-1] S2[y][y+len1]的子串是否是同构的

【分析】

读完题之后一头雾水。先打个暴力?TLE 9

之后只能够想正解。

我们发现两个字符串成映射关系必须满足他们的相邻相同字母的排列方式是相同的。

可能比较绕,我们通过样例来说明一下。

S1: a a b b b c aabbbc aabbbc

S2: d d e e e f ddeeef ddeeef

他们的子串排列方式都是2+3+1的(aa + bbb + c 和 dd + eee + f)

有了这个结论有什么用? 当然有用。

对于每一个字符,我们哈希出他们的位置(位置还能哈希?)

怎么哈希我就不说了吧。

例如 S 1 S1 S1

枚举a时,我们的哈希值是 ( 221111 ) (221111) (221111)

枚举b时,我们的哈希值是 ( 112221 ) (112221) (112221)

枚举c时,我们的哈希值是 ( 111112 ) (111112) (111112)

而其他字符的哈希值都是 ( 111111 ) (111111) (111111)

这样操作又有什么用?用处大着呢

我们发现对于 S 2 S2 S2的哈希值也是 ( 221111 ) , ( 1122221 ) , ( 111112 ) (221111),(1122221),(111112) (221111),(1122221),(111112),其余23个 ( 111111 ) (111111) (111111)

我们发现他们的哈希值也成对应关系

太棒了! 这样问题就可以解了!

大家知道再怎么做么?不需要 O ( 2 6 2 ) O(26^2) O(262)的枚举了。直接搞两个multiset,把26个哈希值扔进去,判两个multiset相同就行了。

算法很牛逼666

【关于哈希的玄学故事】

双哈希在这题上跑的是挺慢的,慢的测试点2300ms,时限是3s,很慌的。

三哈希肯定TLE了

有一位叫做KrK的大佬,过了G题,F题双哈希被Hack了!也是一个红名大佬Hack的

本来这个人看到Rank1

所以写哈希的时候种子最老是rand一下哈。

【代码】

头文件太长请见谅

#include <bits/stdc++.h>
using namespace std ;

#define rep(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define Rep(i, a, b) for (int (i) = (a) - 1; (i) < (b); (i)++)
#define REP(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define Sort(a, len, cmp) sort(a + 1, a + len + 1, cmp)
#define ass(a, sum) memset(a, sum, sizeof(a))

#define ls ((rt) << 1)
#define rs ((rt) << 1 | 1)
#define lowbit(x) (x & -x)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define endl '\n'
#define ENDL cout << endl
#define SZ(x) ((int)x.size())

typedef long long ll ;
typedef unsigned long long ull ;
typedef vector <int> vi ;
typedef pair <int, int> pii ;
typedef pair <ll, ll> pll ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
typedef map <ll, ll> mll ;

const int N = 200010 ;
const double eps = 1e-8 ;
const int iinf = INT_MAX ;
const ll linf = 2e18 ;
const double dinf = 1e30 ;
const int MOD1 = 1000000007 ;
const int MOD2 = 1000000009 ;
const int b1 = 41 ;
const int b2 = 37 ;

inline int read(){
    int X = 0, w = 0 ;
    char ch = 0 ;
    while (!isdigit(ch)) { w |= ch == '-' ; ch = getchar() ; }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar() ;
    return w ? - X : X ;
}

void write(int x){
     if (x < 0) putchar('-'), x = - x ;
     if (x > 9) write(x / 10) ;
     putchar(x % 10 + '0') ;
}

void print(int x) {
    cout << x << endl ;
    exit(0) ;
}

void PRINT(string x) {
    cout << x << endl ;
    exit(0) ;
}

void douout(double x){
     printf("%lf\n", x + 0.0000000001) ;
}

pii H[30][N] ;
int pw1[N], pw2[N] ;
int n, m ;
char str[N] ;

pii Get(pii H[], int l, int r) {
    pii ans = H[r] ;
    l-- ;
    if (l >= 0) {
        ans.fi = (ans.fi - ll(H[l].fi) * pw1[r - l] % MOD1 + MOD1) % MOD1 ;
        ans.se = (ans.se - ll(H[l].se) * pw2[r - l] % MOD2 + MOD2) % MOD2 ;
    }
    return ans ;
}

signed main() {
    pw1[0] = pw2[0] = 1 ;
    scanf("%d%d", &n, &m) ;
    scanf("%s", str + 1) ;
    for (int i = 1; i <= n; i++) {
        pw1[i] = 1ll * pw1[i - 1] * b1 % MOD1 ;
        pw2[i] = 1ll * pw2[i - 1] * b2 % MOD2 ;
    }
    for (int i = 1; i <= n; i++)
    for (int j = 0; j < 26; j++) {
        H[j][i].fi = (ll(H[j][i - 1].fi) * b1 + ll(1 + ('a' + j == str[i]))) % MOD1 ;
        H[j][i].se = (ll(H[j][i - 1].se) * b2 + ll(1 + ('a' + j == str[i]))) % MOD2 ;
    }
    while (m--) {
        int x, y, len ;
        scanf("%d%d%d", &x, &y, &len) ;
        multiset <pii> A, B ;
        for (int i = 0; i < 26; i++) {
            A.insert(Get(H[i], x, x + len - 1)) ;
            B.insert(Get(H[i], y, y + len - 1)) ;
        }
        printf("%s\n", A == B ? "YES" : "NO") ;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值