#pragma once
#include"../data-structure/sparse-table.hpp"// remind: SA including empty string// verify https://judge.yosupo.jp/submission/240structSuffixArray{int_size;vector<int>sa;string&s;SuffixArray(string&str):_size(str.size()),s(str){s.push_back(0);sa.resize(s.size());iota(begin(sa),end(sa),0);sort(begin(sa),end(sa),[&](inta,intb){returns[a]==s[b]?a>b:s[a]<s[b];});vector<int>classes(s.size()),c(s.begin(),s.end()),cnt(s.size());for(intlen=1;len<(int)s.size();len<<=1){for(inti=0;i<(int)s.size();i++){if(i>0&&c[sa[i-1]]==c[sa[i]]&&sa[i-1]+len<(int)s.size()&&c[sa[i-1]+len/2]==c[sa[i]+len/2]){classes[sa[i]]=classes[sa[i-1]];}else{classes[sa[i]]=i;}}iota(begin(cnt),end(cnt),0);copy(begin(sa),end(sa),begin(c));for(inti=0;i<(int)s.size();i++){ints1=c[i]-len;if(s1>=0)sa[cnt[classes[s1]]++]=s1;}classes.swap(c);}s.pop_back();}voidoutput(){cout<<"SA\tidx\tstr"<<endl;for(inti=0;i<size();i++){cout<<i<<": \t"<<sa[i]<<" \t";if(sa[i]!=_size)cout<<s.substr(sa[i],_size-sa[i])<<endl;elsecout<<"$"<<endl;}cout<<endl;}intsize()const{return_size+1;}intoperator[](intk)const{returnsa[k];}};structLCPArray{constSuffixArray&SA;vector<int>LCP,rank;LCPArray(constSuffixArray&sa):SA(sa){LCP.resize(SA.size());rank.resize(SA.size());for(inti=0;i<SA.size();i++){rank[SA[i]]=i;}LCP[0]=0;for(inti=0,h=0;i<SA.size()-1;i++){intj=SA[rank[i]-1];h?h--:h;while((i>j?i:j)+h<SA.size()-1&&SA.s[i+h]==SA.s[j+h]&&++h);LCP[rank[i]-1]=h;}}voidoutput(){cout<<"SA\tidx\tLCP\tstr"<<endl;for(inti=0;i<SA.size();i++){cout<<i<<"\t"<<SA[i]<<" \t"<<LCP[i]<<"\t";if(SA[i]==SA.size()-1)cout<<"$";elsecout<<SA.s.substr(SA[i],SA.size()-1-SA[i]);cout<<endl;}}};// verify// https://onlinejudge.u-aizu.ac.jp/status/users/NyaanNyaan/submissions/1/ALDS1_14_D/judge/3874273/C++14// https://atcoder.jp/contests/abc135/submissions/7574225// https://judge.yosupo.jp/submission/241// https://atcoder.jp/contests/abc141/submissions/7577295structStringSearch{string&s;constSuffixArray&sa;constLCPArray&lcp;SparseTable<int>sparse;StringSearch(LCPArray&lcp):s(lcp.SA.s),sa(lcp.SA),lcp(lcp),sparse(lcp.LCP){}pair<int,int>comp(conststring&t,intlen,intsi,intti=0){intsn=(int)s.size(),tn=(int)t.size();si+=len,ti+=len;while(si<sn&&ti<tn){if(s[si]!=t[ti])returnmake_pair(s[si]<t[ti],ti);si++,ti++;}returnmake_pair((si>=sn&&ti<tn),ti);}pair<int,int>find_range(intleft,intmed,intright,intlen){{intng=left-1,ok=med;while(ng+1<ok){intcur=(ng+ok)/2;if(sparse.query(cur,med)>=len)ok=cur;elseng=cur;}left=ok;}{intok=med,ng=right+1;while(ok+1<ng){intcur=(ng+ok)/2;if(sparse.query(med,cur)>=len)ok=cur;elseng=cur;}right=ok;}returnmake_pair(left,right);}public:// Longest Common Prefix between S[i , N) and S[j , N)intArbitraryLCP(inti,intj){if(i==j)return(int)(s.size())-i;returnsparse.query(min(lcp.rank[i],lcp.rank[j]),max(lcp.rank[i],lcp.rank[j]));}intArbitaryLCP(inti,intj){returnArbitraryLCP(i,j);}// String Search O(|T| + log |S|)// return : [l, r], l and r are indices of Suffix Array// if T doesn't exist, return (-1, -1)pair<int,int>find(string&t){intleft=1,right=sa.size()-1,med=left;intleftlen=0,rightlen=0,tlen=t.size();pair<int,int>ret;while(left+1<right){med=(left+right)/2;intcorres_len=max(min(leftlen,sparse.query(left,med)),min(rightlen,sparse.query(med,right)));if(corres_len<max(leftlen,rightlen)){if(leftlen<rightlen)left=med,leftlen=corres_len;elseright=med,rightlen=corres_len;continue;}ret=comp(t,corres_len,sa[med]);if(ret.second==tlen)returnfind_range(left,med,right,tlen);if(ret.first==0)right=med,rightlen=ret.second;elseleft=med,leftlen=ret.second;}if(sa.size()<=3){if(comp(t,0,sa[left]).second==tlen)returnfind_range(left,left,right,tlen);if(comp(t,0,sa[right]).second==tlen)returnfind_range(left,right,right,tlen);returnmake_pair(-1,-1);}med=left+right-med;ret=comp(t,min(leftlen,rightlen),sa[med]);if(ret.second==tlen)returnfind_range(left,med,right,tlen);returnmake_pair(-1,-1);}};// Usage:// SuffixArray sa(S);// LCPArray lcp(sa);// StringSearch search(lcp);
#line 2 "string/suffix-array.hpp"
#line 2 "data-structure/sparse-table.hpp"
#include<cassert>
#include<limits>
#include<vector>usingnamespacestd;template<typenameT>structSparseTable{inlinestaticconstexprTINF=numeric_limits<T>::max()/2;intN;vector<vector<T>>table;Tf(Ta,Tb){returnmin(a,b);}SparseTable(){}SparseTable(constvector<T>&v):N(v.size()){intb=1;while((1<<b)<=N)++b;table.push_back(v);for(inti=1;i<b;i++){table.push_back(vector<T>(N,INF));for(intj=0;j+(1<<i)<=N;j++){table[i][j]=f(table[i-1][j],table[i-1][j+(1<<(i-1))]);}}}// [l, r)Tquery(intl,intr){assert(0<=landl<=randr<=N);if(l==r)returnINF;intb=31-__builtin_clz(r-l);returnf(table[b][l],table[b][r-(1<<b)]);}};/**
* @brief Sparse Table
*/#line 6 "string/suffix-array.hpp"
// remind: SA including empty string// verify https://judge.yosupo.jp/submission/240structSuffixArray{int_size;vector<int>sa;string&s;SuffixArray(string&str):_size(str.size()),s(str){s.push_back(0);sa.resize(s.size());iota(begin(sa),end(sa),0);sort(begin(sa),end(sa),[&](inta,intb){returns[a]==s[b]?a>b:s[a]<s[b];});vector<int>classes(s.size()),c(s.begin(),s.end()),cnt(s.size());for(intlen=1;len<(int)s.size();len<<=1){for(inti=0;i<(int)s.size();i++){if(i>0&&c[sa[i-1]]==c[sa[i]]&&sa[i-1]+len<(int)s.size()&&c[sa[i-1]+len/2]==c[sa[i]+len/2]){classes[sa[i]]=classes[sa[i-1]];}else{classes[sa[i]]=i;}}iota(begin(cnt),end(cnt),0);copy(begin(sa),end(sa),begin(c));for(inti=0;i<(int)s.size();i++){ints1=c[i]-len;if(s1>=0)sa[cnt[classes[s1]]++]=s1;}classes.swap(c);}s.pop_back();}voidoutput(){cout<<"SA\tidx\tstr"<<endl;for(inti=0;i<size();i++){cout<<i<<": \t"<<sa[i]<<" \t";if(sa[i]!=_size)cout<<s.substr(sa[i],_size-sa[i])<<endl;elsecout<<"$"<<endl;}cout<<endl;}intsize()const{return_size+1;}intoperator[](intk)const{returnsa[k];}};structLCPArray{constSuffixArray&SA;vector<int>LCP,rank;LCPArray(constSuffixArray&sa):SA(sa){LCP.resize(SA.size());rank.resize(SA.size());for(inti=0;i<SA.size();i++){rank[SA[i]]=i;}LCP[0]=0;for(inti=0,h=0;i<SA.size()-1;i++){intj=SA[rank[i]-1];h?h--:h;while((i>j?i:j)+h<SA.size()-1&&SA.s[i+h]==SA.s[j+h]&&++h);LCP[rank[i]-1]=h;}}voidoutput(){cout<<"SA\tidx\tLCP\tstr"<<endl;for(inti=0;i<SA.size();i++){cout<<i<<"\t"<<SA[i]<<" \t"<<LCP[i]<<"\t";if(SA[i]==SA.size()-1)cout<<"$";elsecout<<SA.s.substr(SA[i],SA.size()-1-SA[i]);cout<<endl;}}};// verify// https://onlinejudge.u-aizu.ac.jp/status/users/NyaanNyaan/submissions/1/ALDS1_14_D/judge/3874273/C++14// https://atcoder.jp/contests/abc135/submissions/7574225// https://judge.yosupo.jp/submission/241// https://atcoder.jp/contests/abc141/submissions/7577295structStringSearch{string&s;constSuffixArray&sa;constLCPArray&lcp;SparseTable<int>sparse;StringSearch(LCPArray&lcp):s(lcp.SA.s),sa(lcp.SA),lcp(lcp),sparse(lcp.LCP){}pair<int,int>comp(conststring&t,intlen,intsi,intti=0){intsn=(int)s.size(),tn=(int)t.size();si+=len,ti+=len;while(si<sn&&ti<tn){if(s[si]!=t[ti])returnmake_pair(s[si]<t[ti],ti);si++,ti++;}returnmake_pair((si>=sn&&ti<tn),ti);}pair<int,int>find_range(intleft,intmed,intright,intlen){{intng=left-1,ok=med;while(ng+1<ok){intcur=(ng+ok)/2;if(sparse.query(cur,med)>=len)ok=cur;elseng=cur;}left=ok;}{intok=med,ng=right+1;while(ok+1<ng){intcur=(ng+ok)/2;if(sparse.query(med,cur)>=len)ok=cur;elseng=cur;}right=ok;}returnmake_pair(left,right);}public:// Longest Common Prefix between S[i , N) and S[j , N)intArbitraryLCP(inti,intj){if(i==j)return(int)(s.size())-i;returnsparse.query(min(lcp.rank[i],lcp.rank[j]),max(lcp.rank[i],lcp.rank[j]));}intArbitaryLCP(inti,intj){returnArbitraryLCP(i,j);}// String Search O(|T| + log |S|)// return : [l, r], l and r are indices of Suffix Array// if T doesn't exist, return (-1, -1)pair<int,int>find(string&t){intleft=1,right=sa.size()-1,med=left;intleftlen=0,rightlen=0,tlen=t.size();pair<int,int>ret;while(left+1<right){med=(left+right)/2;intcorres_len=max(min(leftlen,sparse.query(left,med)),min(rightlen,sparse.query(med,right)));if(corres_len<max(leftlen,rightlen)){if(leftlen<rightlen)left=med,leftlen=corres_len;elseright=med,rightlen=corres_len;continue;}ret=comp(t,corres_len,sa[med]);if(ret.second==tlen)returnfind_range(left,med,right,tlen);if(ret.first==0)right=med,rightlen=ret.second;elseleft=med,leftlen=ret.second;}if(sa.size()<=3){if(comp(t,0,sa[left]).second==tlen)returnfind_range(left,left,right,tlen);if(comp(t,0,sa[right]).second==tlen)returnfind_range(left,right,right,tlen);returnmake_pair(-1,-1);}med=left+right-med;ret=comp(t,min(leftlen,rightlen),sa[med]);if(ret.second==tlen)returnfind_range(left,med,right,tlen);returnmake_pair(-1,-1);}};// Usage:// SuffixArray sa(S);// LCPArray lcp(sa);// StringSearch search(lcp);