传送门:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2030
与前一篇博客同样的思路,首先有用的信息只有字符串的前四个字符和后四个字符。同样需要将字符串转化成对应的数字。先抛开如何存信息的问题,谈谈如何"接龙"。假设信息已经存好了,那么肯定是一个头对应一个尾。我们假设程序是这样存储的:
那么从第一个"成语"的"尾巴"开始,搜以这个数字为开头的"成语",由于成语的开头或者成语的结尾可能有重复,所以需要for一边所有这个开头的成语。这样一层层搜下去,每次找到下一个就将接龙长度+1。head1 - tail1
head2 - tail2
head3 - tail3
...
关于成语的存储相当简单。开一个结构体,存的是以这个成语开头的所有结尾、结尾数量以及结尾时候被用过。
struct node由于每次输入一个成语都要检查时候已经登记过开头,为了高效率,再开一个map存放所有成语的开头:
{
int val; // 成语开头
int num; // 结尾的数量
int next[30]; // 以这个字开头的成语的所有结尾
int used[30]; // 这个结尾是否被使用过
}tt[30];
map<int, int> mp;
// 如输入成语bbbbccccdddd
// aaaa转化为对应的整数是18279,且这个开头没有被存储过
// 若当前已经用掉了3个位置,即tt[0]、tt[1]和tt[2]都有成语了
// 那么新成语的开头应该放到tt[3]这个位置了
// 那么mp[18279] = 3;
完整代码:
#include<iostream>
#include<map>
using namespace std;
struct node
{
int val; // 成语开头
int num; // 结尾的数量
int next[30]; // 以这个字开头的成语的所有结尾
int used[30]; // 这个结尾是否被使用过
}tt[30];
//成语的头所在的tt数组的位置
map<int, int> mp;
int ind, re, ans;
void dfs(int cur)
{
if(mp.find(tt[cur].val) == mp.end())
return;
for(int i = 0; i < tt[cur].num; i++)
{
if(!tt[cur].used[i])
{
re++;
ans = ans < re ? re : ans;
tt[cur].used[i] = 1;
dfs(mp[tt[cur].next[i]]);
re--;
tt[cur].used[i] = 0;
}
}
}
int main()
{
int n;
char str[35];
while(~scanf("%d\n", &n))
{
mp.clear();
ind = 0; // 用来标记有多少个不同的成语的"开头"
for(int i = 0; i < 30; i++)
{
tt[i].num = 0;
memset(tt[i].used, 0, sizeof(tt[i].used));
}
for(int i = 0; i < n; i++)
{
scanf("%s", str);
int len = strlen(str);
int head = 0, tail = 0;
// 将成语的头和为转化成相应的整数
for(int j = 0; j < 4; j++)
head = head * 26 + str[j] - 'a';
for(int j = len-4; j < len; j++)
tail = tail * 26 + str[j] - 'a';
if(mp.find(head) != mp.end())// 成语的开头重复了,只需在这个头上再登记个结尾
{
tt[mp[head]].next[tt[mp[head]].num++] = tail;
}
else // 新成语
{
tt[ind].val = head;
tt[ind].next[tt[ind].num++] = tail;
mp[head] = ind;
ind++;
}
}
re = ans = 0;
dfs(0);
printf("%d\n", ans);
}
return 0;
}