题意: 输入n个字符串,两两调用一次strcmp(),问字符比较的总次数。
分析:两个相同字符 比较2次
两个不同字符 比较1次
‘\0’和'\0‘ 比较两次
'\0'和其它字符 比较一次
思路: 建一颗字典树,然后每insert一个字符串,就计算该字符串要与前面加入的字符串共比较多少次,这样就能得到最后的答案。
但是这个题有个坑点,节点太多,在最坏的情况下有4000*1000个节点,每个节点56个不同的字符,那么明显会MLE的,而且这
样也会TLE的。所以要使用压缩算法(左兄弟,右孩子字典树)。建树的方式可以看下图,一看应该就明白了。
代码如下:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define nn 4000100
int head[nn]; //第i个结点的左儿子编号
int son[nn]; //第i个结点的右兄弟编号
char ch[nn]; //第i个结点上的字符
int tot[nn]; //第i个结点为根的子树包含的叶结点总数
int sz;
ll ans;
struct Trie
{
void init()
{
sz = 1;
ans = 0;
ch[0] = tot[0] = head[0] = son[0] = 0;
}
void insert(char *s)
{
int u = 0,v;
int n = strlen(s);
for (int i = 0; i <= n; i++)
{
for (v = head[u]; v; v = son[v])
if (ch[v] == s[i])
break;
if (!v)
{
v = sz++;
tot[v] = 0;
ch[v] = s[i];
son[v] = head[u];
head[u] = v;
head[v] = 0;
}
ans += (ll)(tot[u] - tot[v])*(2 * i + 1);
if (n == i)
{
ans +=(ll) tot[v] * (2 * i + 2);
tot[v]++;
}
tot[u]++;
u = v;
}
}
};
char str[4010];
int main()
{
int tt = 1,n;
while (scanf("%d", &n) &&n)
{
Trie t;
t.init();
for (int i = 0; i < n; i++)
{
scanf("%s", str);
t.insert(str);
}
printf("Case %d: %lld\n", tt++, ans);
}
return 0;
}