给定若干字符串,输出两两strcmp比较的时候每一次比较了多少次
UVA 11732
题目链接https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2832
说实话...这个想起来就头疼
我们知道strcmp实际上是从头比较到尾,也就是说实际上比较一次on也就可以了..但是这个输入会有23兆
然后我们就可以思考,实际上strcmp的比较就是a[i]和b[i]相比,必然是对应位置上面的比较.回想一下trie的构建过程,实际上对于一棵字典树,当且仅当有一个位置不同的时候我们才会将其进行一个分叉,总共的比较次数实际上也就是访问路上走过的路
所以就想到了对于所有的字符串构建这样的一棵字典树,然后进行一次dfs,递归到叶子节点之后,再返回父亲节点,将情况返回回去.
因为我们已经选定过一边之后我们仍要获得另一边的关系,这个获取是盲目的,所以在获取之后我们又要将结果除2
差不多了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxnode = 4e6 + 7;
const int sigma_size = 26;
struct Trie{
int head[maxnode];//head[i]即i节点的左儿子是什么
int next[maxnode];//next[i]保存了它的右边的兄弟.
char ch[maxnode];
int tot[maxnode];tot[i]表示i节点的大小
int sz;
long long ans;
void clear() {
sz = 1;
tot[0] = head[0] = next[0] = 0;
}
void insert(const char *s){
int u = 0, v, n = strlen(s);
tot[0] ++;
for (int i = 0;i <= n;i ++){
bool found = false;
for (v = head[u]; v; v = next[v]){
if (ch[v] == s[i]){
found = true;
break;
}
}
if (!found){
v = sz ++;
tot[v] = 0;
ch[v] = s[i];
next[v] = head[u];
head[u] = v;
head[v] = 0;
}
u = v;
tot[u] ++;
}
}
void dfs(int depth, int u){
if (head[u] == 0){
ans += tot[u] * (tot[u] - 1) * depth;//递归到叶子,访问父亲是否有分叉,有分叉就纳入答案
}
else{
int sum = 0;
for (int v = head[u]; v; v = next[v]){
sum += tot[v] *(tot[u] - tot[v]);//作为父亲节点,选取一边的答案,那么实际上另一边的答案可以直接相乘
}
ans += sum / 2 * (2 * depth + 1);//但是由于这样的选择是盲目的,所以我们需要将其除2
for (int v = head[u]; v ; v = next[v]){
dfs(depth + 1, v);
}
}
}
long long count(){
ans = 0;
dfs(0, 0);
return ans;
}
};
const int maxl = 1010;
int n;
char word[maxl];
Trie trie;
int main(){
int kase = 1;
while (~scanf("%d", &n), n ){
trie.clear();
for (int i = 0;i < n;i ++){
scanf("%s", word );
trie.insert(word);
}
printf("Case %d: %lld\n", kase ++, trie.count());
}
return 0;
}