两个字符串匹配时,如果对应位置相同,则需要比较两次(第二次判断是否为'\0') ,若失败只需比较一次。
因此,把所有字符串建成一个字典树,每个节点用num记录有多少个字符串经过这里。
那么对于这个位置,考虑匹配成功的情况,共发生num×(num-1)/2×2次比较。
对于这个节点的每一个后继结点,设该节点的num值为tnum,那么在前驱节点上有num-tnum个和该节点字符不相同的字符,比较的次数为(num-tnum)×tnum。对每个后继节点都求出这个值,做和,因为两两间比较都被算了两次,再除二,就是匹配不成功的比较次数。
深度遍历这颗树,对每个节点都求这两个值做和即可。
注意:由于大多数节点上可能只有一两个后继节点,不能写成节点中开一个数组ch[26]这种写法,会T掉。要写成邻接表的形式。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define LL long long
int idx(char c){
return c-'a';
}
struct node{
LL num;
char ch;
node(){
num=0;
};
};
vector <int> G[3000000];
node N[3000000];
int Cn;
void init(){
int t=0;
for(int i=0;i<Cn;i++){
G[i].clear();
N[i].num=0;
}
Cn=1;
}
void input(char s[]){
int cur=0;
int len=strlen(s);
N[cur].num++;
for(int i=0;i<=len;i++){
int next=0;
for(int j=0;j<G[cur].size();j++){
int tmp=G[cur][j];
if(N[tmp].ch==s[i]){
next=tmp;
break;
}
}
if(!next){
N[Cn].ch=s[i];
G[cur].push_back(Cn);
next=Cn;
Cn++;
}
N[next].num++;
cur=next;
}
}
LL solve(int n){
LL res=0;
LL tmp=0;
if(n) res+=N[n].num*(N[n].num-1);
for(int i=0;i<G[n].size();i++){
int t=G[n][i];
res+=solve(t);
tmp+=(N[n].num-N[t].num)*N[t].num;
}
res+=tmp/2;
return res;
}
int main(){
int T;
int kase=1;
while(~scanf("%d",&T)){
if(!T) break;
init();
for(int i=0;i<T;i++){
char s[1005];
scanf("%s",s);
input(s);
}
printf("Case %d: %lld\n",kase++,solve(0));
}
return 0;
}
本文深入探讨了字符串匹配技术,通过构建字典树优化比较过程,显著减少了不必要的比较次数,提高了效率。文章详细阐述了算法原理、实现细节及应用案例。
1968

被折叠的 条评论
为什么被折叠?



