土下座しながら探索中

主に競技プログラミング

AOJ 2614 : Almost Same Substring

問題概要 : 略

解法 :
SuffixArray + LCP + RMQ で頑張った

与えられた2つの文字列を結合する
その際に間には大きな文字を入れる
S = S + "$" + T' + "$"
この S の SuffixArray, LCP を作成する
LCP 内の区間[a,b)の最小値を求めるRMQを用意する

左から1つずつ見ていく
今見ている場所 i から i+|T'|-1 までの文字列が T' と何文字異なるかを判定する
異なる文字数を diff とする、初期値は0
LCP内の区間[min(iのある場所,S内でT'が始まる場所),max(iのある場所,S内でT'が始まる場所) ) の最小値だけ先頭が一致しているため、その文だけ先にすすめる
最小値が0の時、先頭の1文字が一致していないので diff を1増やし先頭を1つ先に移動する
diff >= 2 になった時、条件を満たさないため終わる
全て終わった後に diff が1かどうかで判定

今回は1文字異なるかどうかだが、この方法なら任意の文字数だけ異なるかどうかを ( 異なる文字数 ) * 2 回の処理で求めることができる

コード :

#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)

using namespace std;

const int IINF = INT_MAX;

class RMQ{
private:
  int limit,N;
  vector<int> dat;
public:
  void init(int n_){
    N = n_;
    limit = 1;
    while(limit<n_)limit*=2;
    dat.clear();
    dat.resize(2*limit,IINF);
  }
  
  int _build(int cur,int L,int R,const vector<int> &buf){
    if( !( 0 <= cur && cur < 2*limit ) ) return IINF;
    if( L == R-1 ) {
      if( L >= N ) return IINF;
      dat[cur] = buf[L];
    } else {
      int vl = _build(cur*2+1,L,(L+R)/2,buf);
      int vr = _build(cur*2+2,(L+R)/2,R,buf);
      dat[cur] = min(vl,vr);
    }
    return dat[cur];
  }

  void build(const vector<int> &buf){ _build(0,0,limit,buf); };
 
  int _query(int a,int b,int k,int l,int r){
    if(r<=a || b<=l)return IINF;
    else if(a<=l && r<=b)return dat[k];
    int vl = _query(a,b,k*2+1,l,(l+r)/2);
    int vr = _query(a,b,k*2+2,(l+r)/2,r);
    return min(vl,vr);
  }

  int query(int a,int b){ return _query(a,b,0,0,limit); }
};

string s,t;
vector<int> SuffixArray,LCP,vec;
RMQ rmq;

struct CompareSA{
  int k;
  const vector<int> &rank;
  CompareSA(const int k,const vector<int> &rank) : k(k), rank(rank){}
  bool operator()(int i,int j) {
    if( rank[i] != rank[j] ) return rank[i] < rank[j];
    int ri = i + k < (int)rank.size() ? rank[i+k] : -1;
    int rj = j + k < (int)rank.size() ? rank[j+k] : -1;
    return ri < rj;
  }
};
  
vector<int> build_sa(const string &s){
  int n = s.size();
  vector<int> sa(n+1), rank(n+1), tmp(n+1);
  rep(i,sa.size()){
    sa[i] = i;
    rank[i] = i < n ? s[i] : -1;
  }
  for(int k=1;k<=n;k*=2){
    CompareSA comp(k,rank);
    sort(sa.begin(),sa.end(),comp);
    tmp[sa[0]] = 0;
    for(int i=1;i<=n;i++) tmp[sa[i]] = tmp[sa[i-1]] + comp(sa[i-1],sa[i]);
    rank = tmp;
  }
  return sa;
}
  
vector<int> build_lcp(const string &s,vector<int> sa){
  int n = s.size(), h = 0;
  vector<int> rank(n+1), lcp(n+1);
  rep(i,n+1) rank[sa[i]] = i;
  rep(i,n) {
    int j = sa[rank[i]-1];
    if( h > 0 ) h--;
    for(; j + h < n && i + h < n; h++ ) if( s[j+h] != s[i+h] ) break;
    lcp[rank[i]-1] = h;
  }
  return lcp;
}

inline bool isValid(int a,int b,int c,int d){
  if( ( b - a ) != ( d - c ) ) return false;
  if( a == c && b == d ) return true;
  int p1 = a, p2 = c, diff = 0;
  while( diff <= 1 && a <= b ){
    int L1 = vec[a];
    int L2 = vec[c];
    int coef = rmq.query(min(L1,L2),max(L1,L2));
    if( coef == 0 ) {
      ++diff, ++a, ++c;
      continue;
    }
    a += coef, c += coef;

  }
  return diff == 1;
}


int main(){
  cin >> s >> t;
  int n = s.size(), m = t.size();
  s = s + "$" + t + "$";
  SuffixArray = build_sa(s);
  LCP         = build_lcp(s,SuffixArray);
  vec.resize(SuffixArray.size()+1,-1);
  rmq.init(LCP.size());
  rmq.build(LCP);

  rep(i,SuffixArray.size()) vec[SuffixArray[i]] = i;

  int answer = 0;
  rep(i,n-m+1) if( isValid(i,i+m-1,n+1,n+1+m-1) ) ++answer;
  cout << answer << endl;
  return 0;
}