• D. Tavas and Malekas 解析(字串匹配)


    Codeforce 535 D. Tavas and Malekas 解析(字串匹配)

    今天我們來看看CF535D
    題目連結

    題目
    給你一個字串(p)和一些(index)代表字串(p)在哪些位置會和長度為(n)的字串(s)匹配,求有多少種(s)的可能性。

    前言

    我還是只會(hash)

    想法

    (p)沒有覆蓋到的(index)數量記做(cnt),如果那些會重疊的(p)沒有矛盾的話,那答案就是(26^{cnt}),因為有(cnt)的格子都是完全自由的。
    這題有三種作法,都是要判斷重疊的(p)有沒有矛盾。注意,我們每次判斷一個從(y[i])開始的(p)字串有沒有和前面重疊時,只需要和從(y[i-1])開始的(p)字串比較就好,畢竟如果(y[i-2])開始的(p)字串和(y[i-1])開始的(p)字串沒有矛盾的話,那麼(y[i-2])開始的(p)字串和(y[i])開始的(p)字串就不會有矛盾(如果(y[i-1])開始的字串和(y[i])開始的字串沒有矛盾)。
    三種匹配字串的方式是:

    1. Rolling hash,直接比對子字串的hash值看看一不一樣。
    2. KMP,只需要維護失敗函數,由於失敗函數代表一個最長的前綴的長度使得在(j-1)結束的字串的後綴和前綴一樣,那麼我們只要看看(p)字串的重疊部分是否沒有矛盾就好。
    3. Z-Algorithm,和KMP如出一轍,只不過這次(Z)函數代表從(j)開始往後的字串和整個字串的最長前綴長度。一樣只要看看(p)字串的重疊部分是否沒有矛盾就好。

    程式碼(Rolling hash):

    const int _n=1e6+10;
    int t,n,m;
    const int _N = _n;  // str len
    const int _p1 = 31, _M = 1000000009;
    int pnM[_N] = {0};  // p^n mod M
    int hp[_N] = {0};   // hash prefix
    char p[_N];
    int lenp,y[_n];
    
    inline void genpnM() {
      int res = 1;  //p^0%M
      pnM[0] = 1;
      for (int i = 1; i < _N; i++) pnM[i] = res = (res * 1ll * _p1) % _M;
    }
    inline void genhp() {  //hp[n]=hash prefix[0,n)
      int res = 0;
      hp[0] = 0;  //[0,0) is empty string
      for (int i = 1; i <= lenp; i++) hp[i] = res = (res * 1ll * _p1 + p[i - 1] * 1ll) % _M;
    }
    inline int dhash(char s[]) {  //direct hash
      int len = strlen(s);
      int res = 0;
      for (int i = 1; i <= len; i++) res = (res * 1ll * _p1 + s[i - 1] * 1ll) % _M;
      return res;
    }
    inline int hashlr(int l, int r) {  //[l,r)
      int tmp = hp[r] * 1ll - pnM[r - l] * 1ll * hp[l] % _M;
      if (tmp < 0) tmp += _M;
      return tmp;
    }
    //以上是rolling hash模板
    main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
      cin>>n>>m>>p;lenp=strlen(p);rep(i,0,m){cin>>y[i];y[i]--;}
      genpnM();genhp();
      int cnt=y[0];rep(i,1,m){
        if(y[i-1]+lenp-1>=y[i]){
          if(hashlr(y[i]-y[i-1],lenp)!=hashlr(0,lenp-(y[i]-y[i-1]))){
            cout<<0<<'
    ';return 0;
          }
        }
        else cnt+=y[i]-(y[i-1]+lenp-1)-1;
      }cnt+=n-(y[m-1]+lenp);
      if(m==0)cnt=n;
      cout<<powmod(26,cnt)<<'
    ';
      return 0;
    }
    

    標頭、模板請點Submission看
    Submission

    程式碼(KMP):

    const int _n=1e6+10;
    int t,n,m;
    char p[_n];
    int lenp,y[_n],jj,F[_n];
    bool vis[_n];
    main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
      cin>>n>>m>>p;lenp=strlen(p);rep(i,0,m){cin>>y[i];y[i]--;}
      /*F[0]=-1,jj=-1;rep(i,0,lenp){
        jj=F[i];
        while(jj!=-1 and p[i]!=p[jj])jj=F[jj];
        F[i+1]=jj+1; //if jj==-1, F[i]=0
      }*/ //也行
      F[0]=-1;
      for(int i = 0, j = -1; i < lenp; F[++i] = ++j) { //計算失敗函數,一般來說i<lenp-1,但是這題我們需要i<lenp
        while (j != -1 && p[i] != p[j]) j = F[j];
      }
      jj=F[lenp];
      while(jj!=-1){ //看看哪些前綴可以和整條字串p的後綴匹配
        vis[jj]=1;
        jj=F[jj];
      }
      int cnt=y[0];rep(i,1,m){
        if(y[i-1]+lenp-1>=y[i]){
          if(!vis[lenp-(y[i]-y[i-1])]){
            cout<<0<<'
    ';return 0;
          }
        }
        else cnt+=y[i]-(y[i-1]+lenp-1)-1;
      }cnt+=n-(y[m-1]+lenp);
      if(m==0)cnt=n;
      cout<<powmod(26,cnt)<<'
    ';
      return 0;
    }
    

    標頭、模板請點Submission看
    Submission

    程式碼(Z-Algorithm):

    const int _n=1e6+10;
    int t,n,m,lenp,Z[_n],y[_n];
    char p[_n];
    main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
      cin>>n>>m>>p;lenp=strlen(p);rep(i,0,m){cin>>y[i];y[i]--;}
      int L = 0, R = 0;
      for (int i = 1; i < lenp; i++) {
        if (i > R) {
          L = R = i;
          while (R < n && p[R-L] == p[R]) R++;
          Z[i] = R-L; R--;
        } else {
          int k = i-L;
          if (Z[k] < R-i+1) Z[i] = Z[k];
          else {
            L = i;
            while (R < n && p[R-L] == p[R]) R++;
            Z[i] = R-L; R--;
          }
        }
      }
      int cnt=y[0];rep(i,1,m){
        if(y[i-1]+lenp-1>=y[i]){
          if(Z[y[i]-y[i-1]]!=lenp-(y[i]-y[i-1])){
            cout<<0<<'
    ';return 0;
          }
        }
        else cnt+=y[i]-(y[i-1]+lenp-1)-1;
      }cnt+=n-(y[m-1]+lenp);
      if(m==0)cnt=n;
      cout<<powmod(26,cnt)<<'
    ';
      return 0;
    }
    

    標頭、模板請點Submission看
    Submission

  • 相关阅读:
    eclipse+myeclipse 使用技巧备忘
    程序员的自我修养
    枚举工具类 EnumUtils
    日期/时间处理工具 DateTimeUtil
    轻松了解Spring中的控制反转和依赖注入(一)
    了解SpringMVC框架及基本工作流程
    HTTP请求行、请求头、请求体详解
    Tomcat项目部署问题记录
    入手IntelliJ IDEA 常用配置
    解决阿里云OSS跨域问题
  • 原文地址:https://www.cnblogs.com/petjelinux/p/13593945.html
Copyright © 2020-2023  润新知