版权声明:本文是博主乱写的文章,可以随便转载 https://blog.csdn.net/weixin_43768644/article/details/89601126
题目链接:https://vjudge.net/problem/UVA-11732
参考自:liuzibujian 的博客 https://blog.csdn.net/zengchen__acmer/article/details/24416217
解题思路:
①先说求得比较次数的思路,不考虑怎么建字典树:
(1)按照题中给的strcmp函数,for循环中比较一次,如果当前两字符不相同,那么判断if那个句子。
(2)也就是说 在第i位不同,就比较 2*(i-1)+1 = 2*i-1次
(3)那么我们建一颗trie树,每个节点储存当前情况下延伸出去的子节点的个数,记做 trie【i】.sum
遍历当前节点的子节点与当前正在插入的字符,不管如何,该字符和所有子节点储存的字母都会比较一次
设当前节点为U,那么 ans += trie【U】.sum
(4)如果子节点中有和当前插入字符匹配的,设有匹配的那个字符的节点为j,那么聪明的你肯定知道为什么要
ans += trie【j】.sum 了,因为这代表对每个在strcmp函数进行if那个句子的比较的数量
②理解了思路,那么就要建立trie树,按照https://blog.csdn.net/weixin_43768644/article/details/89501622
的方法建字典树方法会mle,因为需要4000*1001*字符种类 的空间
所以学习一下什么左儿子右兄弟的方法构造一下trie树。
具体请在代码中看。我觉的有点像链式前向星,son看做head数组,right看做next数组
举个例子,空trie树插入“black”
再插入“blank”
差不多就这样。
本题代码:(具体可能有些细节还没讲,相信你可以看懂!)
#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);
}
}