转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
题目:有两个字符串集合,从第一个集合中取某个串的非空前缀,从第二个集合中取某个串的非空后缀,拼接成一个串,问有多少个不同的新串。
大概是N年前训练的题。。。然后不会做,就没管了
最近波波好同学问了御坂姐姐,然后就会做了,其实很水。。。
为何这么弱。。。。
首先把第一个集合中的串加入到Trie中,然后就可以枚举前缀了,然后就是统计后缀,麻烦的是可能出现重复的。
当我们按长度枚举前缀的时候,可以考虑如果后缀的前缀出现在前缀的后继中,那便不用考虑,后面总会统计到。
这样的话,其实只要比较第一个字母。
如果当前结点P表示一个前缀串,那么如果p -> next[i]为空,说明前缀中不含字母i为后继,那么我们就可以统计以i开头的后缀了。否则总能在p -> next[i]为前缀中统计到。
有一点需要注意的是,如果 p -> next[i]便是我们最终的串,即以p 为前缀时,后缀只有一个字母的话,那就不能把这一个字母拉入到前缀中,因为后缀是非空的。所以再处理下end[i],表示是否存在以单个字母i为后缀的。
至于统计以i开头的不重复后缀的话,可以反向加入到Trie中统计。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
const int M = 100005;
const int N = 1005;
int n , m , cnt[26] , end[26];
char a[N][N] , b[N][N];
LL ans = 0;
struct Trie {
int tot;
struct trie {
trie *next[26];
}s[M] , *root;
trie *newnode () {
trie *p = &s[tot ++];
memset (p -> next , 0 , sizeof(p -> next));
return p;
}
void init () {
memset (cnt , 0 , sizeof(cnt));
tot = 0;
root = newnode ();
}
void insert (char *s) {
trie *p = root;
for (int i = 0 ; s[i] ; i ++) {
int c = s[i] - 'a';
if (p -> next[c] == NULL) {
p -> next[c] = newnode ();
}
p = p -> next[c];
}
}
void cal (trie *p) {
for (int i = 0 ; i < 26 ; i ++) {
if (p -> next[i]) {
cnt[i] ++;
cal (p -> next[i]);
}
}
}
void gao (trie *p) {
for (int i = 0 ; i < 26 ; i ++) {
if (p -> next[i] == NULL) {
if (p != root) ans += cnt[i];
}
else {
if (p != root && end[i]) ans ++;
gao (p -> next[i]);
}
}
}
}suffix , prefix;
int main () {
while (scanf ("%d %d" , &n , &m) != EOF && n + m) {
prefix.init ();suffix.init ();
ans = 0;
memset (cnt , 0 , sizeof (cnt));
memset (end , 0 , sizeof (end));
for (int i = 0 ; i < n ; i ++) {
scanf ("%s" , a[i]);
prefix.insert (a[i]);
}
for (int i = 0 ; i < m ; i ++) {
scanf ("%s" , b[i]);
reverse (b[i] , b[i] + strlen(b[i]));
end[b[i][0] - 'a'] = 1;
suffix.insert (b[i]);
}
suffix.cal (suffix.root);
prefix.gao (prefix.root);
printf ("%lld\n" , ans);
}
return 0;
}