zoukankan      html  css  js  c++  java
  • 51 NOD 1753 相似子串 字符串hash

     
      1735  相似子串 
    基准时间限制:5 秒 空间限制:131072 KB 分值: 80
     
    两个字符串相似定义为:
    1.两个字符串长度相等
    2.两个字符串对应位置上有且仅有至多一个位置所对应的字符不相同
    给定一个字符串,每次询问两个子串在给定的规则下是否相似。给定的规则指每次给出一些等价关系,如‘a'=’b',‘b'=’c'等,注意这里的等价关系具有传递性,即若‘a'=’b',‘b'=’c',则‘a'=’c'。
    Input
    第一行一个字符串s(1<=|s|<=300000)
    第二行一个整数T(1<=T<=300000)
    对于每次询问:
    第一行5个整数k,l1,r1,l2,r2,表示有k个等价规则,询问的是子串[l1,r1],[l2,r2](1<=k<=10,1<=l1<=r1<=|s|,1<=l2<=r2<=|s|)
    接下来k行每行两个连续的字符表示这两个字符等价。
    此题中所有的字符均为小写字母。
    Output
    T行,若相似则输出“YES”否则输出“NO”
    Input示例
    abac
    3
    1 1 2 3 4
    bc
    1 1 2 3 4
    ac
    1 1 2 2 3
    ac
    Output示例
     
    YES
    YES
    NO

    题解:
      hash值是由一系列字符* base^i 组成的,这里首先按照字母归类(如“aba”,按“a”归类即为“a”*( base^1+base^3 ))
      如果只是判断在给定规则下是否相等,那么可以把归类后26个字母对应的hash值按照并查集里的等价关系进行累加,
        若某一联通块所对应的hash值的总和在两个串中不相等则两个串在给定规则下不相等,否则相等。
       考虑只有一个位置不同的情况,那么必然是存在两个联通块对应的hash在两个串上不相等,且这两个差值是base的某个幂次的两个相反数,
      那么预处理一下base所有次幂并存下即可。
      时间复杂度O((|S|+T)*26)
    #include <bits/stdc++.h>
    inline long long read(){long long x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
    using namespace std;
    typedef unsigned long long ULL;
    
    const int  N = 4e5+10;
    const ULL mod = 10000019ULL;
    
    ULL sqr[N], tmp = 1, pre[N][27], fi[50], se[50];
    map<ULL, int> mp;
    char s[N], ch[50];
    int T, fa[50], vis[50], F[50];
    int finds(int x) {return fa[x] == x? x:fa[x]=finds(fa[x]);}
    int main() {
        scanf("%s%d",s+1,&T);
        int n = strlen(s+1);
        sqr[0] = 1;
        for(int i = 1; i <= n; ++i) sqr[i] = sqr[i-1] * mod, mp[sqr[i]] = 1,mp[(-sqr[i])] = 1;
        for(int i = 1; i <= n; ++i) {
            tmp = tmp * mod;
            pre[i][s[i] - 'a' + 1] += tmp;
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= 26; ++j) pre[i][j] += pre[i-1][j];
        }
        for(int t = 1; t <= T; t++) {
            int k,l1,r1,l2,r2,cnt = 0;
            scanf("%d%d%d%d%d",&k,&l1,&r1,&l2,&r2);
            for(int i = 1; i <= 26; ++i) fa[i] = i,vis[i] = 0;
            for(int i = 1; i <= 26; ++i) fi[i] = pre[r1][i] - pre[l1-1][i];
            for(int i = 1; i <= 26; ++i) se[i] = pre[r2][i] - pre[l2-1][i];
            for(int i = 1; i <= k; ++i) {
                scanf("%s",ch);
                int fx = finds(ch[0] - 'a' + 1);
                int fy = finds(ch[1] - 'a' + 1);
                if(fx  > fy) swap(fx,fy);
                if(fx != fy)
                    fa[fy] = fx,fi[fx] += fi[fy],se[fx] += se[fy],fi[fy] = 0,se[fy] = 0;
            }
            if(r1 - l1 != r2 - l2) {
                printf("NO
    ");
                continue;
            }
            for(int i = 1; i <= 26; ++i) {
                int nfa = finds(i);
                if(fi[nfa]*sqr[l2 - l1] != se[nfa] && !vis[nfa])
                    F[++cnt] = i, vis[nfa] = 1;
            }
            if(cnt == 0) printf("YES
    ");
            else {
                if(cnt > 2) printf("NO
    ");
                else {
                    if(fi[F[1]]*sqr[l2 - l1] + fi[F[2]]*sqr[l2 - l1]== se[F[2]] + se[F[1]]) {
                        if(mp[(fi[F[1]]*sqr[l2 - l1] - se[F[1]])])printf("YES
    ");
                        else printf("NO
    ");
                    }
                    else printf("NO
    ");
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    JNI和NDK的关系
    JNI和NDK的关系
    Android SDK结构分析
    设计模式:单例模式
    编程规范:占位符
    设计模式:工厂模式
    代码整洁之道----读书笔记
    个人编程规范
    装饰器函数
    异常处理
  • 原文地址:https://www.cnblogs.com/zxhl/p/7491541.html
Copyright © 2011-2022 走看看