zoukankan      html  css  js  c++  java
  • bzoj 1461 字符串匹配

    Bzoj 1461 字符串的匹配
    给两个长度为n、m的序列A、B,问A中有多少个子串与B等价(相同位置的值排名相同)

    题解:同样考虑hash。因为A是子序列,值的排名难以修改,多以用把排名用线段树的位置维护。Hash=sigma(id *base^sort[i]);同样可以比较两串是否相等。注意如果有很多相同值的细节问题:要把他们按位置先后排名

    不过这样因为取模常数过大,bz上很难过

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 using namespace std;
      6 #define maxn 500020
      7 #define maxm 10020
      8 #define mod 1000000007
      9 #define __O3 __attribute__((optimize("O3")))
     10 typedef long long LL;
     11 struct node{
     12     int add,ls,rs,sz;
     13     LL hash;
     14 }sgt[maxm * 4];
     15 int tot,n,m,s,root;
     16 int a[maxn],b[maxn],num[maxn],ans,id[maxn],vis[maxn];
     17 LL pow[maxn],Fpow[maxn],hash_b,inv;
     18 const LL p = 31;
     19 
     20 __O3 void pre(){
     21     //memcpy(num,b,sizeof(b));
     22     for (int i = 1 ; i <= m ; i++) num[i] = b[i];
     23     sort(b + 1,b + m + 1);
     24     for (int i = 1 ; i <= m ; i++){
     25         int now = num[i];   
     26         num[i] = lower_bound(b + 1,b + m + 1,num[i]) - b;
     27         num[i] += vis[now];
     28         vis[now]++;
     29     }
     30 }
     31 __O3 void build(int &now,int l,int r){
     32     now = ++tot;
     33     if ( l == r ) return;
     34     int mid = (l + r) >> 1;
     35     build(sgt[now].ls,l,mid);
     36     build(sgt[now].rs,mid + 1,r);
     37 }
     38 __O3 inline void add(int now,int d){
     39     sgt[now].add += d; 
     40     sgt[now].hash = ((sgt[now].hash + (LL) d * (Fpow[sgt[now].sz] - 1)) % mod + mod) % mod;
     41 }
     42 __O3 inline void pushdown(int now){
     43     if ( sgt[now].add != 0 ){
     44         if ( sgt[now].ls ) add(sgt[now].ls,sgt[now].add);
     45         if ( sgt[now].rs ) add(sgt[now].rs,sgt[now].add);
     46         sgt[now].add = 0;
     47     }
     48 }
     49 __O3 inline void update(int now){
     50     sgt[now].sz = sgt[sgt[now].ls].sz + sgt[sgt[now].rs].sz;
     51     sgt[now].hash = (sgt[sgt[now].ls].hash + sgt[sgt[now].rs].hash * pow[sgt[sgt[now].ls].sz]) % mod;
     52 }
     53 //在某个值域上加一个新的位置,或删除一个位置
     54 __O3 void modify(int now,int l,int r,int pos,int d){ //线段树:以值域为位置,每个值域记录在是他的id这和。值域表示的是排名
     55     if ( l == r ){
     56         if ( d < 0 ) sgt[now].sz-- , sgt[now].hash = (((sgt[now].hash + (LL)d * pow[1]) % mod + mod) % mod * inv) % mod;
     57         if ( d > 0 ) sgt[now].sz++ , sgt[now].hash = (sgt[now].hash + (LL)d * pow[sgt[now].sz]) % mod;    
     58         return;
     59     }
     60     pushdown(now);
     61     int mid = (l + r) >> 1;
     62     if ( pos <= mid ) modify(sgt[now].ls,l,mid,pos,d);
     63     else modify(sgt[now].rs,mid + 1,r,pos,d);
     64     update(now);
     65 }
     66 __O3 void modify(int now,int l,int r,int ls,int rs,int d){ //修改位置,将整体左移
     67     if ( ls <= l && rs >= r ){
     68         add(now,d);
     69         return;
     70     }
     71     pushdown(now);
     72     int mid = (l + r) >> 1;
     73     if ( ls <= mid ) modify(sgt[now].ls,l,mid,ls,rs,d);
     74     if ( rs > mid ) modify(sgt[now].rs,mid + 1,r,ls,rs,d);
     75     update(now);
     76 }
     77 __O3 inline LL power(LL x,int y){
     78     LL res = 1;
     79     while ( y ){
     80         if ( y & 1 ) res = (res * x) % mod;
     81         x = (x * x) % mod;
     82         y >>= 1;
     83     }
     84     return res % mod;
     85 }
     86 __O3 void init(){
     87     pre();
     88     pow[0] = Fpow[0] = 1;
     89     for (int i = 1 ; i <= m ; i++) pow[i] = (pow[i - 1] * p) % mod , Fpow[i] = (Fpow[i - 1] + pow[i]) % mod;
     90     inv = power(p,mod - 2);
     91     for (int i = 1 ; i <= m ; i++) hash_b = (hash_b + pow[num[i]] * (LL) i) % mod;
     92     build(root,1,s);
     93     for (int i = 1 ; i <= m ; i++){
     94         modify(root,1,s,a[i],i);
     95     }
     96 }
     97 __O3 int main(){
     98     freopen("match.in","r",stdin);
     99     freopen("match.out","w",stdout);
    100     scanf("%d %d %d",&n,&m,&s);
    101     for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
    102     for (int i = 1 ; i <= m ; i++) scanf("%d",&b[i]);
    103     init();
    104     for (int i = 2 ; i <= n - m + 1 ; i++){
    105         if ( sgt[root].hash == hash_b ) id[++ans] = i - 1;;
    106         modify(root,1,s,a[i - 1],-1);
    107         modify(root,1,s,1,s,-1);
    108         modify(root,1,s,a[i + m - 1],m);
    109     }
    110     if ( sgt[root].hash == hash_b ) id[++ans] = n - m + 1;
    111     printf("%d
    ",ans);
    112     for (int i = 1 ; i <= ans ; i++) printf("%d
    ",id[i]);
    113     return 0;
    114 }
    View Code

    这道题还有一个解法:kmp + 树状数组,对于每个对应的位置,只需要排名相同就可以匹配,直接用树状数组维护有多少个比当前小,有多少个相等,就可以知道当前位置是否相等。显然每个位置都相等,可以推得所有都相等。注意跳fail时两个串要分别进行对应的修改

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 #define maxn 1000020
     5 #define lowbit(x) (x&(-x))
     6 
     7 int s1[maxn],s2[maxn];
     8 int n,m,a[maxn],b[maxn],S,id[maxn],ans,fail[maxn];
     9 
    10 inline int query(int s[],int d){
    11     int ans = 0;
    12     for (int i = d ; i >= 1 ; i -= lowbit(i)) ans += s[i];
    13     return ans;
    14 }
    15 inline void add(int s[],int d,int x){
    16     for (int i = d ; i <= S ; i += lowbit(i)) s[i] += x;
    17 }
    18 inline bool jud(int x,int y){
    19     if ( query(s1,x - 1) == query(s2,y - 1) && query(s1,x) == query(s2,y) ) return 1;
    20     return 0;
    21 }
    22 void getfail(){
    23     int p = 1,q = 0;
    24     while ( p < m ){
    25         if ( !jud(b[p + 1],b[q + 1]) ){
    26             int now = q;
    27                q = fail[q];
    28             for (int i = now ; i > q ; i--){
    29                 add(s2,b[i],-1);    
    30             }
    31             for (int i = p - now + 1 ; i < p - q + 1 ; i++){
    32                 add(s1,b[i],-1);
    33             }        
    34         }
    35         else fail[++p] = ++q , add(s1,b[p],1) , add(s2,b[q],1);
    36         if ( !q && !jud(b[p + 1],b[q + 1]) ) p++;
    37     }
    38 //    for (int i = 1 ; i <= m ; i++) cout<<fail[i]<<" ";
    39 //    cout<<endl;
    40 }
    41 void kmp(){
    42     for (int i = 0 ; i <= S ; i++) s1[i] = s2[i] = 0;
    43     int p = 0,q = 0;
    44     while ( p < n ){
    45         if ( !jud(a[p + 1],b[q + 1]) || q == m ){
    46             int now = q;
    47                q = fail[q];
    48             for (int i = now ; i > q ; i--){
    49                 add(s2,b[i],-1);
    50             }
    51             for (int i = p - now + 1 ; i < p - q + 1 ; i++){
    52                 add(s1,a[i],-1);
    53             }
    54         }
    55         else p++, q++, add(s1,a[p],1), add(s2,b[q],1);
    56         if ( q == m ) id[++ans] = p - m + 1;
    57         if ( !q && !jud(a[p + 1],b[q + 1]) ) p++;
    58     }
    59 }
    60 int main(){
    61     freopen("input.txt","r",stdin);
    62     scanf("%d %d %d",&n,&m,&S);
    63     for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
    64     for (int i = 1 ; i <= m ; i++) scanf("%d",&b[i]);
    65     //b[m + 1] = '#';
    66     getfail();
    67     kmp();
    68     printf("%d
    ",ans);
    69     for (int i = 1 ; i <= ans ; i++) printf("%d
    ",id[i]);
    70     return 0;
    71 }
    View Code

    总结:字符串hash是用来比较字符串的重要方法,可见hash还可以用来比较某些特定意义下的相等:如排名相同即相等。有两种hash方法,一种是一位置为关键字,一种是以排名为关键字。第一种通常实用,但第二种可以在位置比较确定的时候用来比较两个字符串的相对顺序关系。还是要根据题目来定

  • 相关阅读:
    USACO2008 Cow Cars /// oj23323
    USACO2008 Roads Around The Farm /// queue oj23321
    USACO2007 捕牛记 /// queue+桶 oj1503
    哈理工多校算法赛一
    USACO2012 Haybale stacking /// 区间表示法 oj21556
    USACO2012 Broken necklace /// DP oj10103
    USACO2004 cube stacking /// 带权并查集 oj1302
    ACM-ICPC 2018 南京赛区网络预赛 J sum (找一个数拆成两个无平方因子的组合数)
    分层图 (可以选择K条路的权为0,求最短路)
    POJ 3537 Crosses and Crosses(sg博弈)
  • 原文地址:https://www.cnblogs.com/zqq123/p/5283204.html
Copyright © 2011-2022 走看看