树上倍增求LCA
const int N = 1e5+5;
int lca[N][20];
int dep[N];
void dfs(int now,int f,int depth){///初始时dfs(根节点,0,1);
lca[now][0] = f;
dep[now] = depth;
for (int i=head[now];i!=0;i=edge[i].last){//链式前向星
int v = edge[i].to;
if (v==f) continue;
dfs(v,now,depth+1);
}
}
void getst(int n){
for (int j=1;(1<<j)<=n;j++)
for (int i=1;i<=n;i++)//这里千万别写成i+(1<<j)-1<=n,因为这不是维护区间
lca[i][j] = lca[lca[i][j-1]][j-1];
}
int getlca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int differ = dep[x]-dep[y];
for (int i=0;(1<<i)<=differ;i++){
if ((1<<i)&differ){
x = lca[x][i];
}
}
if (x==y) return x;//忘了这行的话答案去下面传父亲就不对了
for (int i=18;i>=0;i--){
if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
}
return lca[x][0];
}
最小表示法
///求所有循环同构中字典序最小或者最大
int getmin(int len)
{
int i=0,j=1,k=0;
while (i<len && j<len && k<len){
int t = s[(i+k)%len] - s[(j+k)%len];
if (!t) k++;
else {
if (t<0) j += k+1;
else i += k+1;
if (i==j) j++;
k = 0;
}
}
return i<j?i:j;
}
int getmax(int len)
{
int i=0,j=1,k=0;
while (i<len && j<len && k<len){
int t = s[(i+k)%len] - s[(j+k)%len];
if (!t) k++;
else {
if (t>0) j += k+1;
else i += k+1;
if (i==j) j++;
k = 0;
}
}
return i<j?i:j;
}
Manacher
精简版
void manacher(char* s){
//init
ss[0] = '@';
int len = strlen(s);
for1(i,1,len){
ss[2*i-1] = '#';
ss[2*i] = s[i-1];
}
ss[2*len+1]='#';
ss[2*len+2]='$';
ss[2*len+3] = '\0';
len = len*2+4;
int ans = 0;
int pos=1,maxp=1,j;
pal[0] = pal[1] = 1;
for (int i=2;i<len;i++){
if (pal[2*pos-i]<maxp-i+1) pal[i] = pal[2*pos-i];
else {
j = maxp-i+1;
//if (j<=1) j = 1;
while (i-j>=0 && i+j<len && ss[i+j]==ss[i-j]) j++;
pal[i] = j,pos = i,maxp = max(maxp,i+pal[i]-1);
ans = max(ans,pal[i]-1);
}
}
printf("%d\n",ans);
}
复杂版
const int maxn=1000010;
char str[maxn];//原字符串
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
int i,len=strlen(st);
tmp[0]='@';///开头加一个不等于#也不等于字符串的字符,这样就不用判断左边越界了,那么右边万一比到n+1怎么办呢?有\0吗?不,\0在n处,解决办法看16行
for(i=1;i<=2*len;i+=2)
{
tmp[i]='#';
tmp[i+1]=st[i/2];
}
tmp[2*len+1]='#';
tmp[2*len+2]='$';///结尾搞一个不是@也不是#的字符
tmp[2*len+3]=0;
return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(st[i-Len[i]]==st[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
ans=max(ans,Len[i]);
}
return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
Len[st+ed]-1>=ed-st+1判断区间是否回文
EXKMP
精简版
void exkmp(char *a,char *b){
//getnt
int pos=0,maxp=0,j;
int lena = strlen(a),lenb = strlen(b);
nxt[0] = lenb;
for (int i=1;i<lenb;i++){
if (nxt[i-pos]<maxp-i+1) nxt[i] = nxt[i-pos];
else {
j = maxp-i+1;//if (j<0) j = 0;
while (i+j<lenb && b[i+j]==b[j]) j++;
nxt[i] = j,pos = i,maxp = max(maxp,max(i+nxt[i]-1,i));
}
}
//getex
pos = -1,maxp = -1;
for (int i=0;i<lena;i++){
if (nxt[i-pos]<maxp-i+1) ex[i] = nxt[i-pos];
else {
j = maxp-i+1;//if (j<0) j = 0;
while (i+j<lena && j<lenb && a[i+j]==b[j]) j++;
ex[i] = j,pos = i,maxp = max(maxp,max(i+ex[i]-1,i));
}
}
}
不精简版
const int maxn=100010; //字符串长度最大值
int nt[maxn],ex[maxn]; //ex数组即为extend数组
///预处理计算next数组
void GETNEXT(char *str)
{
int i=0,j,po,len=strlen(str);
nt[0]=len;///用自己作为后缀与自己匹配
while(i+1<len && str[i]==str[i+1]) i++;///暴力求next[1]
nt[1]=i;
po=1;///从此点出发next数组延伸位置最远
for(i=2;i<len;i++)
{
if(nt[i-po]< nt[po]+po-i )///第一种情况,可以直接得到next[i]的值
nt[i]=nt[i-po];
else///第二种情况,要继续匹配才能得到next[i]的值
{
j=nt[po]+po-i;
if(j<0)j=0; ///小于0表示没有已知相同部分,重新开始匹配
while(i+j<len&&str[j]==str[j+i])
j++;
nt[i]=j;
po=i;///更新po的位置
}
}
}
///计算extend数组
void EXKMP(char *s1,char *s2)
{
int i=0,j,po,len=strlen(s1),l2=strlen(s2);
GETNEXT(s2);
while(i<l2&&i<len&&s1[i]==s2[i])
i++;
ex[0]=i;
po=0;
for(i=1;i<len;i++)
{
if(nt[i-po]<ex[po]+po-i)
ex[i]=nt[i-po];
else
{
j=ex[po]+po-i;
if(j<0)j=0;
while(i+j<len&&j<l2&&s1[j+i]==s2[j])
j++;
ex[i]=j;
po=i;
}
}
}
KMP
#include<bits/stdc++.h>
const int N = 1e6+5;
using namespace std;
int nt[N];
char s[N];
char ss[N];
int KMP() ///求大串中有几个子串
{
int len=strlen(s);
int t=strlen(ss);
nt[0]=-1;
int cnt=0;
///构造next数组
for (int i=0,j=-1; i<t; ){
if (j==-1 || ss[i]==ss[j]){
i++;
j++;
nt[i]=j;
}
else j=nt[j];
}
for (int i=0;i<t;i++) printf("nt[%d]=%d ",i,nt[i]); printf("\n");
///开始遍历大串找其中的小串
for (int i=0,j=0; i<len; ){
if (j==-1 || ss[j]==s[i]){
i++;
j++;
}
else j=nt[j];
if (j==t){
cnt++;
j=0;
}
}
return cnt;
}
int main()
{
while (cin>>s){
cin>>ss;
cout<<KMP()<<endl;
}
}
字典树---普通版本
int trie[maxnode][sigma_size];///maxnode表示节点数,maxnode=单词数*每个单词最大长度(最坏情况每个单词组合都不一样)
///sigma_size 表示一个节点最多延伸出多少各节点,也就是字符种类
/// trie[i][j] 的值表示 i节点指向j这个数字对应字母 的节点的位置
bool val[maxnode]; ///为真表示当前节点是一个单词的结束
int sz;
int idx(char ch) { return ch-'a';}//将字母转换成对应数字,如果还有大写,标点的话要更麻烦些
void init(int x){
val[x] = false;
memset(trie[x],0,sizeof trie[x]);
}
void insert(char *s){///插入
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!trie[u][v]){
init(sz);///初始化多组输入时上一次残留的数据
trie[u][v] = sz++;
}
u = trie[u][v];
}
val[u] = true;
}
bool query(char *s){
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!trie[u][v]) return false; ///这个节点还没有开辟
u = trie[u][v];
}
return true;
}
领接表存字典树
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N = 4000*1001 + 5;
struct node
{
int son;
int right;
int sum;
char ch;
}trie[N];
int id;
ll ans;
void init()
{
ans = 0;
id = 1;
trie[0].right = 0;
trie[0].son = 0;
trie[0].sum = 0;
}
void insert(char *s)
{
int u = 0,j;
int len = strlen(s);
for (int i=0;i<=len;i++){
bool flag = false;
for (j=trie[u].son;j!=0;j=trie[j].right){
if (s[i]==trie[j].ch){
flag = true;
break;
}
}
if (!flag){
j = id++;
trie[j].right = trie[u].son;
trie[u].son = j;
trie[j].ch = s[i];
trie[j].son = 0;
trie[j].sum = 0;
}
ans += (trie[u].sum+trie[j].sum);
if (i==len) trie[j].sum++;
trie[u].sum++;
u = j;
}
}
int main()
{
int n;
char in[1010];
for (int kca=1;scanf("%d",&n),n;kca++){
init();
while (n--) scanf("%s",in),insert(in);
printf("Case %d: %lld\n",kca,ans);
}
}
后缀数组求两个串LCS----最长公共子串
#include<cstdio>
#include<algorithm>
#include<cstring>
#define debug(x) printf("----Line%s----\n",#x)
using namespace std;
const int N = 1e5+5;
int wa[N<<1],wb[N<<1],sa[N<<1],rnk[N<<1],cnt[N<<1],height[N<<1];
char s[N<<1];
void build(int n,int m)
{
int *x=wa,*y=wb,p,i,j;
for (i=0;i<m;i++) cnt[i] = 0;
for (i=0;i<n;i++) cnt[x[i]=s[i]]++;
for (i=1;i<m;i++) cnt[i] += cnt[i-1];
for (i=n-1;i>=0;i--) sa[--cnt[x[i]]] = i;
for (j=1,p=1;p<n;j<<=1,m=p){
for (p=0,i=n-j;i<n;i++) y[p++] = i;
for (i=0;i<n;i++) if (sa[i]>=j) y[p++] = sa[i]-j;
for (i=0;i<m;i++) cnt[i] = 0;
for (i=0;i<n;i++) cnt[x[y[i]]]++;
for (i=1;i<m;i++) cnt[i] += cnt[i-1];
for (i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]] = y[i];
swap(x,y);
x[sa[0]] = 0;
p = 1;
for (i=1;i<n;i++)
x[sa[i]] = ( y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j] )? p-1:p++;
}
}
void getheight(int n)
{
int k=0;
for (int i=0;i<n;i++) rnk[sa[i]] = i;
for (int i=0;i<n;i++){
if (rnk[i]==0){height[rnk[i]]=k=0;continue;}
if (k) k--;
int j = sa[rnk[i]-1];
while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;
height[rnk[i]] = k;
}
}
int main()
{
int n;
while (~scanf("%s",s)){
int len = strlen(s);
scanf("%s",s+len+1);
n = len + strlen(s+len+1) + 1;///+1很关键,虽然这题数据太水不+1也会AC
build(n,200);//debug(1);
getheight(n);//debug(2);
int ans = 0;
for (int i=1;i<n;i++)
if ( (sa[i]<len && sa[i-1]>len) || (sa[i]>len && sa[i-1]<len) )
ans = max(height[i],ans);
//debug(3);
printf("%d\n",ans);
}
return 0;
}
后缀数组查询两段区间LCP,ST维护height
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define debug(x) printf("----line %s----\n",#x)
using namespace std;
const int N = 2e5 + 5;
int sa[N],wa[N],wb[N],rnk[N],cnt[N],height[N];
char s[N];
int st[N][20];
void buildsa(int n,int m)///m是初始字符种类,可以偏大
{
int *x = wa,*y = wb,p,i,j;
for (int i=0;i<m;i++) cnt[i] = 0;
for (int i=0;i<n;i++) cnt[x[i]=s[i]]++;
for (int i=1;i<m;i++) cnt[i] += cnt[i-1];
for (int i=n-1;i>=0;i--) sa[--cnt[x[i]]] = i;
for (j=1,p=1;p<n;j<<=1,m=p){
for (p=0,i=n-j;i<n;i++) y[p++] = i;
for (int i=0;i<n;i++) if (sa[i]>=j) y[p++] = sa[i]-j;///得出根据第二关键字排名的
for (int i=0;i<m;i++) cnt[i] = 0;
for (int i=0;i<n;i++) cnt[x[y[i]]]++;
for (int i=1;i<m;i++) cnt[i] += cnt[i-1];
for (int i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]] = y[i];
swap(x,y);
x[sa[0]] = 0;
p = 1;
for (i=1;i<n;i++)
x[sa[i]] = ( y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j] )? p-1:p++;
}
}
void getheight(int n)
{
int