先整理各路大神的题解 Orz,以后再埋坑
SP7586 NUMOFPAL - Number of Palindromes
Description
求一个串中包含几个回文串。
Input
输入一个字符串S
Output
包含的回文串的个数.
思路一:
用马拉车求出预处理后以每个字母处的回文半径P[i],遍历一遍,ans=ans+P[i]/2,最终ans就是答案
答案是以每一位为中心的回文串长度/2的和,(如果添加字符则为回文半径长度/2。)
不能理解的话,可以看下这个
# a # a # b # a # a #
1 2 3 2 1 6 1 2 3 2 1
数字对应于每一个位置的回文半径
.(实际上减去1才是,但是计算的时候要加上1,所以代码里面直接用了这个.)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #define R register 6 7 using namespace std; 8 9 const int maxn=1008; 10 11 char ch[maxn]; 12 char s[maxn<<2]; 13 int len,RL[maxn<<2],MaxRight,center,ans; 14 int main() 15 { 16 scanf("%s",ch); 17 len=strlen(ch); 18 for(R int i=0;i<len;i++)s[2*i+1]=ch[i]; 19 len=2*len+1; 20 for(R int i=0;i<len;i++) 21 { 22 if(i<=MaxRight) 23 RL[i]=min(RL[2*center-i],MaxRight-i); 24 else RL[i]=1; 25 while(s[i-RL[i]]==s[i+RL[i]] and i-RL[i]>=0 and i+RL[i]<len) 26 RL[i]++; 27 if(i+RL[i]-1>MaxRight) 28 MaxRight=i+RL[i]-1,center=i; 29 } 30 for(R int i=0;i<len;i++)ans+=RL[i]/2; 31 printf("%d",ans); 32 }
我自己写的代码:
1 #include <iostream> 2 #include <string> 3 #include <string.h> 4 #include <algorithm> 5 #include <vector> 6 7 using namespace std; 8 int sum; 9 10 void Manacher(string s) 11 { 12 sum=0; 13 /*改造字符串*/ 14 string res="$#"; 15 for(int i=0;i<s.size();++i) 16 { 17 res+=s[i]; 18 res+="#"; 19 } 20 21 /*数组*/ 22 vector<int> P(res.size(),0); 23 int mi=0,right=0; //mi为最大回文串对应的中心点,right为该回文串能达到的最右端的值 24 int maxLen=0,maxPoint=0; //maxLen为最大回文串的长度,maxPoint为记录中心点 25 26 for(int i=1;i<res.size();++i) 27 { 28 //关键句,文中对这句以详细讲解 29 if(right>i) 30 P[i]=min(P[2*mi-i],right-i); 31 else 32 P[i]=1; 33 // P[i]=right>i ?min(P[2*mi-i],right-i):1; 34 35 while(res[i+P[i]]==res[i-P[i]]) 36 ++P[i]; 37 38 if(right<i+P[i]) //超过之前的最右端,则改变中心点和对应的最右端 39 { 40 right=i+P[i]; 41 mi=i; 42 } 43 44 if(maxLen<P[i]) //更新最大回文串的长度,并记下此时的点 45 { 46 maxLen=P[i]; 47 maxPoint=i; 48 } 49 sum+=P[i]/2; 50 } 51 // return s.substr((maxPoint-maxLen)/2,maxLen-1); //要返回最长子串就把函数类型改为string 52 printf("%d\n",sum); 53 } 54 int main() 55 { 56 string str; 57 cin>>str; 58 Manacher(str); 59 }
思路二:
回文自动机板子题,每次在LAST上CNT++,,最后从父亲累加儿子的CNT,因为如果fa[v]=u,则u一定是v的子回文串; 统计答案
1 #include<set> 2 #include<map> 3 #include<ctime> 4 #include<queue> 5 #include<cmath> 6 #include<cstdio> 7 #include<vector> 8 #include<cstring> 9 #include<cstdlib> 10 #include<iostream> 11 #include<algorithm> 12 #define ll unsigned long long 13 #define mem(a,b) memset(a,b,sizeof(a)) 14 #define mec(a,b) memcpy(a,b,sizeof(b)) 15 using namespace std; 16 const int MAXN=1000+50; 17 char str[MAXN]; 18 int len; 19 ll ans; 20 int hd[MAXN],ect; 21 struct Edge{int nx,to,w;}ar[MAXN]; 22 void adds(int u,int v,int w){ar[++ect].to=v,ar[ect].w=w,ar[ect].nx=hd[u],hd[u]=ect;} 23 struct PAM 24 { 25 int tot,last,n; 26 int fa[MAXN],ln[MAXN],s[MAXN],cnt[MAXN]; 27 void init() 28 { 29 fa[0]=fa[1]=1; 30 ln[tot=1]=-1;ln[last=n=ect=0]=0; 31 s[0]=-1; 32 } 33 int ge(int u,int w) 34 {for(int e=hd[u],v=ar[e].to;e;v=ar[e=ar[e].nx].to)if(ar[e].w==w)return v;return 0;} 35 int gf(int k){while(s[n-ln[k]-1]^s[n])k=fa[k];return k;} 36 inline void extend(int x,int c) 37 { 38 s[++n]=c;last=gf(last); 39 if(!ge(last,c)) 40 { 41 fa[++tot]=ge(gf(fa[last]),c); 42 ln[tot]=ln[last]+2; 43 adds(last,tot,c); 44 45 } 46 cnt[last=ge(last,c)]++; 47 } 48 void calc() 49 { 50 for(int i=tot;i>=2;i--) 51 { 52 cnt[fa[i]]+=cnt[i]; 53 ans+=cnt[i]; 54 } 55 } 56 }pam; 57 int main() 58 { 59 60 pam.init(); 61 scanf("%s",str+1);len=strlen(str+1); 62 //for(int i=1;i<=len;i++)printf("%c",str[i]); 63 for(int i=1,x;i<=len;i++)pam.extend(i,str[i]-‘a‘); 64 pam.calc(); 65 printf("%lld ",ans); 66 return 0; 67 68 }
思路三:
利用回文树来写,回文树是一种处理回文串的强大工具
推荐学习博客:
回文树介绍(Palindromic Tree)
1 #include <iostream> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <algorithm> 5 #include <math.h> 6 #include <stdio.h> 7 8 using namespace std; 9 #define MAX 1005 10 struct Node 11 { 12 int next[26]; 13 int len; 14 int sufflink; 15 int num; 16 }tree[MAX]; 17 char s[MAX]; 18 19 int num; 20 int suff; 21 bool addLetter(int pos) 22 { 23 int cur=suff,curlen=0; 24 int let=s[pos]-‘a‘; 25 26 while(1) 27 { 28 curlen=tree[cur].len; 29 if(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos]) 30 break; 31 cur=tree[cur].sufflink; 32 } 33 if(tree[cur].next[let]) 34 { 35 suff=tree[cur].next[let]; 36 return false; 37 } 38 num++; 39 suff=num; 40 tree[num].len=tree[cur].len+2; 41 tree[cur].next[let]=num; 42 if(tree[num].len==1) 43 { 44 tree[num].sufflink=2; 45 tree[num].num=1; 46 return true; 47 } 48 while(1) 49 { 50 cur=tree[cur].sufflink; 51 curlen=tree[cur].len; 52 if(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos]) 53 { 54 tree[num].sufflink=tree[cur].next[let]; 55 break; 56 } 57 } 58 tree[num].num=1+tree[tree[num].sufflink].num; 59 return true; 60 61 } 62 void initTree() 63 { 64 num=2;suff=2; 65 tree[1].len=-1;tree[1].sufflink=1; 66 tree[2].len=0;tree[2].sufflink=1; 67 } 68 int main() 69 { 70 scanf("%s",s); 71 int len=strlen(s); 72 initTree(); 73 long long int ans=0; 74 for(int i=0;i<len;i++) 75 { 76 addLetter(i); 77 ans+=tree[suff].num; 78 } 79 printf("%d\n",ans); 80 return 0; 81 }
看看上一题的升级版
The Number of Palindromes
题意:
给出一个字符串,求其不相同回文子串的个数
思路一:
回文自动机模板题,因为回文自动机中每个新建的结点就表示一个回文子串,各个结点都不相同
所以不同回文子串个数就是回文自动机中新增结点个数,直接输出即可
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN = 100005 ; 5 const int N = 26 ; 6 typedef long long LL; 7 struct Palindromic_Tree { 8 int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成 9 int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点 10 LL cnt[MAXN] ; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) 11 int num[MAXN] ; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数 12 int len[MAXN] ;//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串) 13 int S[MAXN] ;//存放添加的字符 14 int last ;//指向新添加一个字母后所形成的最长回文串表示的节点。 15 int n ;//表示添加的字符个数。 16 int p ;//表示添加的节点个数。 17 18 int newnode ( int l ) {//新建节点 19 for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ; 20 cnt[p] = 0 ; 21 num[p] = 0 ; 22 len[p] = l ; 23 return p ++ ; 24 } 25 26 void init () {//初始化 27 p = 0 ; 28 newnode ( 0 ) ; 29 newnode ( -1 ) ; 30 last = 0 ; 31 n = 0 ; 32 S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判 33 fail[0] = 1 ; 34 } 35 36 int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的 37 while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ; 38 return x ; 39 } 40 41 void add ( int c ) { 42 c -= ‘a‘ ; 43 S[++ n] = c ; 44 int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置 45 if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串 46 int now = newnode ( len[cur] + 2 ) ;//新建节点 47 fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转 48 next[cur][c] = now ; 49 num[now] = num[fail[now]] + 1 ; 50 } 51 last = next[cur][c] ; 52 cnt[last] ++ ; 53 } 54 55 void count () { 56 for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ; 57 //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串! 58 } 59 }T; 60 61 int main() 62 { 63 std::ios::sync_with_stdio(false); 64 string a; 65 int t; 66 int ct=1; 67 cin>>t; 68 while(t--) 69 { 70 cin>>a; 71 T.init(); 72 int len=a.size(); 73 for(int i=0;i<len;i++) 74 T.add(a[i]); 75 cout<<"Case #"<<ct++<<": "; 76 cout<<T.p-2<<endl; //输出新增结点个数即可 77 } 78 }
思路二:
后缀数组+马拉车(本质不同回文子串统计)
回文子串可以考虑先来个O(n)的马拉车预处理,这样每个回文子串长度必然是计数,那么我们可以统计本质不同的(正中间的字符+右半边串)回文子串个数。然后可以考虑用后缀自动机统计答案。这道题的关键的关键在于去重的处理。去重要求去掉:h[i]范围内已经被统计过的串。那么可以用一个变量维护 目前已经被统计过的长度。要注意到h数组和马拉车的lc数组是没什么关系的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define rank rk 4 const int MAX = 2e5+10000; 5 char ch[MAX]; 6 int cntA[MAX],cntB[MAX],A[MAX],B[MAX],tsa[MAX],rank[MAX],SA[MAX],lc[MAX],h[MAX]; 7 int n,t; 8 int Cas =1; 9 void init(){ 10 memset(ch,0,sizeof ch); 11 ch[0]=‘z‘+1; 12 } 13 void input(){ 14 scanf("%s",ch+1); 15 n = strlen(ch+1); 16 ch[n*2+1]=‘#‘; 17 for (int i=n;i>=1;i--){ 18 ch[i*2] = ch[i]; 19 ch[i*2-1] =‘#‘; 20 } 21 n = n*2+1; 22 ch[n+1]=‘\0‘; 23 } 24 void get_SA(){ 25 for (int i=0;i<=10000;i++) cntA[i]=0; 26 for (int i=1;i<=n;i++) cntA[ch[i]]++; 27 for (int i=1;i<=10000;i++) cntA[i]+=cntA[i-1]; 28 for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i; 29 rank[SA[1]]=1; 30 for (int i=2;i<=n;i++){ 31 rank[SA[i]]=rank[SA[i-1]]; 32 if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++; 33 } 34 for (int step = 1;rank[SA[n]]<n;step<<=1){ 35 for (int i=0;i<=n;i++)cntA[i]=cntB[i]=0; 36 for (int i=1;i<=n;i++){ 37 cntA[A[i]=rank[i]]++; 38 cntB[B[i]=(i+step<=n)?rank[i+step]:0]++; 39 } 40 for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1]; 41 for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] =i; 42 for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i]; 43 rank[SA[1]]=1; 44 for (int i=2;i<=n;i++){ 45 rank[SA[i]]=rank[SA[i-1]]; 46 if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++; 47 } 48 } 49 } 50 void get_Height(){ 51 for (int i=1,j=0;i<=n;i++){ 52 if (j) j--; 53 while (ch[i+j]==ch[SA[rank[i]-1]+j])j++; 54 h[rank[i]]=j; 55 } 56 } 57 void Manacher(){ 58 lc[1]=1; 59 int k=1; 60 for (int i=2;i<=n;i++){ 61 // printf("%d %d\n",i,k); 62 int p = k+lc[k]-1; 63 if (i<=p){ 64 lc[i]=min(lc[2*k-i],p-i+1); 65 }else{ 66 lc[i]=1; 67 } 68 while (ch[i+lc[i]]==ch[i-lc[i]])lc[i]++; 69 if (i+lc[i]>k+lc[k])k=i; 70 } 71 } 72 void print(){ 73 printf("%s\n",ch+1); 74 for (int i=1;i<=n;i++){ 75 printf("%s %d\n",ch+SA[i],lc[SA[i]]); 76 } 77 } 78 void solve(){ 79 get_SA(); 80 get_Height(); 81 Manacher(); 82 print(); 83 long long res =0; 84 // cout<<"1: "<<res<<endl; 85 int cnt=0; 86 for (int i=2;i<=n;i++){ 87 cnt = min(cnt,h[i]); 88 res+=max(0,lc[SA[i]]-min(h[i],cnt)); 89 // cout<<i<<" "<<res<<endl; 90 if (lc[SA[i]]>cnt){ 91 cnt = lc[SA[i]]; 92 } 93 } 94 // cout<<res/2<<endl; 95 printf("Case #%d: %I64d\n",Cas++,res/2); 96 } 97 int main(){ 98 scanf("%d",&t); 99 while (t--){ 100 init(); 101 input(); 102 solve(); 103 } 104 return 0; 105 }
这道题主要是去重。
一开始我打的去重还是很有问题,还是看别人的才打出来了。
把原串反向加入到原串后面,中间插入特殊字符。后缀数组求sa、height。
分奇偶,奇串和偶串肯定是不同种的。然后对于一个位置i,找到对应位置,用rmq求区间min。
去重:用cnt记录目前计算的回文长度与height的min值(所以这里计算的时候要按字符串大小顺序计算)。
如果有新增回文串,就加进去,并更新cnt。
主要是最后一步,要好好理解。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxl 200010 9 #define INF 0xfffffff 10 11 int c[Maxl]; 12 int n,cl,l; 13 14 int mymin(int x,int y) {return x<y?x:y;} 15 int mymax(int x,int y) {return x>y?x:y;} 16 17 char s[Maxl]; 18 void init() 19 { 20 scanf("%s",s); 21 l=strlen(s);cl=0; 22 for(int i=0;i<l;i++) c[++cl]=s[i]-‘a‘+1; 23 c[++cl]=30; 24 for(int i=l-1;i>=0;i--) c[++cl]=s[i]-‘a‘+1; 25 } 26 27 int sa[Maxl],rk[Maxl],Rs[Maxl],y[Maxl],wr[Maxl]; 28 void get_sa(int m) 29 { 30 memcpy(rk,c,sizeof(rk)); 31 for(int i=0;i<=m;i++) Rs[i]=0; 32 for(int i=1;i<=cl;i++) Rs[rk[i]]++; 33 for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; 34 for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i; 35 36 int ln=1,p=0;//p表示目前有多少个不一样的rk 37 while(p<cl) 38 { 39 int k=0; 40 for(int i=cl-ln+1;i<=cl;i++) y[++k]=i; 41 for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln; 42 for(int i=1;i<=cl;i++) 43 wr[i]=rk[y[i]]; 44 45 for(int i=0;i<=m;i++) Rs[i]=0; 46 for(int i=1;i<=cl;i++) Rs[wr[i]]++; 47 for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; 48 for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i]; 49 50 for(int i=1;i<=cl;i++) wr[i]=rk[i]; 51 for(int i=cl+1;i<=cl+ln;i++) wr[i]=0; 52 p=1,rk[sa[1]]=1; 53 for(int i=2;i<=cl;i++) 54 { 55 if(wr[sa[i]]!=wr[sa[i-1]]||wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++; 56 rk[sa[i]]=p; 57 } 58 ln*=2;m=p; 59 } 60 sa[0]=rk[0]=0; 61 } 62 63 int height[Maxl]; 64 void get_he() 65 { 66 int k=0; 67 for(int i=1;i<=cl;i++) if(rk[i]!=1) 68 { 69 int j=sa[rk[i]-1]; 70 if(k) k--; 71 while(c[i+k]==c[j+k]&&i+k<=cl&&j+k<=cl) k++; 72 height[rk[i]]=k; 73 } 74 height[1]=0; 75 } 76 77 int d[Maxl][20]; 78 void rmq_init() 79 { 80 for(int i=1;i<=cl;i++) d[i][0]=height[i]; 81 for(int j=1;(1<<j)<=cl;j++) 82 for(int i=1;i+(1<<j)-1<=cl;i++) 83 d[i][j]=mymin(d[i][j-1],d[i+(1<<j-1)][j-1]); 84 } 85 86 int rmq(int x,int y) 87 { 88 int t; 89 if(x>y) t=x,x=y,y=t; 90 x++; 91 int k=0; 92 while((1<<(k+1))<=y-x+1) k++; 93 return mymin(d[x][k],d[y-(1<<k)+1][k]); 94 } 95 96 97 int ans; 98 void ffind() 99 { 100 int cnt=0;ans=0; 101 //奇 102 for(int i=1;i<=cl-n;i++) 103 { 104 cnt=mymin(cnt,height[i]); 105 if(sa[i]<=l) 106 { 107 int j=rk[cl-sa[i]+1]; 108 int tem=rmq(i,j); 109 if(tem>cnt) 110 { 111 ans+=(tem-cnt); 112 cnt=tem; 113 } 114 } 115 } 116 //偶 117 cnt=0; 118 for(int i=1;i<=cl-n;i++) 119 { 120 cnt=mymin(cnt,height[i]); 121 if(sa[i]<=l) 122 { 123 int j=rk[cl-sa[i]+2]; 124 int tem=rmq(i,j); 125 if(tem>cnt) 126 { 127 ans+=tem-cnt; 128 cnt=tem; 129 } 130 } 131 } 132 } 133 134 int main() 135 { 136 int T,kase=0; 137 scanf("%d",&T); 138 while(T--) 139 { 140 init(); 141 get_sa(30+n); 142 get_he(); 143 rmq_init(); 144 ffind(); 145 printf("Case #%d: %d\n",++kase,ans); 146 } 147 return 0; 148 } 149 150 [HDU3948]
思路三:
回文树模板,学一学贴一贴啊...
1 #define me AcrossTheSky&HalfSummer11 2 #include <cstdio> 3 #include <cmath> 4 #include <ctime> 5 #include <string> 6 #include <cstring> 7 #include <cstdlib> 8 #include <iostream> 9 #include <algorithm> 10 11 #include <set> 12 #include <stack> 13 #include <queue> 14 #include <vector> 15 16 #define lowbit(x) (x)&(-x) 17 #define Abs(x) ((x) > 0 ? (x) : (-(x))) 18 #define FOR(i,a,b) for((i)=(a);(i)<=(b);(i)++) 19 #define FORP(i,a,b) for(int i=(a);i<=(b);i++) 20 #define FORM(i,a,b) for(int i=(a);i>=(b);i--) 21 #define ls(a,b) (((a)+(b)) << 1) 22 #define rs(a,b) (((a)+(b)) >> 1) 23 #define getlc(a) ch[(a)][0] 24 #define getrc(a) ch[(a)][1] 25 26 #define maxn 100005 27 #define maxc 30 28 #define maxm 100005 29 #define INF 1070000000 30 using namespace std; 31 typedef long long ll; 32 typedef unsigned long long ull; 33 34 template<class T> inline 35 void read(T& num){ 36 num = 0; bool f = true;char ch = getchar(); 37 while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) f = false;ch = getchar();} 38 while(ch >= ‘0‘ && ch <= ‘9‘) {num = num * 10 + ch - ‘0‘;ch = getchar();} 39 num = f ? num: -num; 40 } 41 int outs[100]; 42 template<class T> inline 43 void write(T x){ 44 if (x==0) {putchar(‘0‘); putchar(‘\n‘); return;} 45 if (x<0) {putchar(‘-‘); x=-x;} 46 int num=0; 47 while (x){ outs[num++]=(x%10); x=x/10;} 48 FORM(i,num-1,0) putchar(outs[i]+‘0‘); putchar(‘\n‘); 49 } 50 /*==================split line==================*/ 51 int tot[maxn],num[maxn],len[maxn],fail[maxn],S[maxn],ch[maxn][maxc]; 52 char s[maxn]; 53 int p,last,ans,cnt; 54 int newnode(int l){ 55 tot[p]=0; num[p]=0; len[p]=l; 56 return p++; 57 } 58 void init(){ 59 p=0; memset(ch,0,sizeof(ch)); 60 newnode(0); newnode(-1); 61 last=0; cnt=0; S[cnt]=-1; fail[0]=1; 62 } 63 int get_fail(int x){ 64 while(S[cnt-len[x]-1]!=S[cnt]) x=fail[x]; 65 return x; 66 } 67 void add(int c,int pos){ 68 S[++cnt]=c; 69 int cur=get_fail(last); 70 if (!ch[cur][c]){ 71 int now=newnode(len[cur]+2); 72 fail[now]=ch[get_fail(fail[cur])][c]; 73 ch[cur][c]=now; ans++; 74 } 75 last=ch[cur][c]; 76 tot[last]++; 77 } 78 int main(){ 79 int cas; read(cas); int Cas=1; 80 while (Cas<=cas){ 81 printf("Case #%d: ",Cas); 82 ans=0; 83 scanf("%s",s); 84 int n=strlen(s); 85 init(); 86 FORP(i,0,n-1) add(s[i]-‘a‘+1,i); 87 write(ans); 88 Cas++; 89 } 90 }
原文地址:https://www.cnblogs.com/jiamian/p/11252214.html