/*
注:原题解转载的其他人的,只是加了一些个人想法
1.lin为存单词的数组,t为输入单词的个数,vis数组存的是使用的次数,、
len数组表示没一个单词的长度 。
2.此题目的状态是单词的位置和长度(i,length)
*/
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
struct node{
char lin[1000];
}s[20]; // 结构体 s 用于储存单词
/*
此步解决了保存单词的问题,使可以一个一个字母的保存,
反思也可以在输入一些单词,按单词长度从大到小输出
*/
int t,ans;
int vis[20]; //标记数组,每一次搜索初始化为 0,单词使用一次加一,超过二就不能继续使用 ,即小于等于2.
int len[20]; //单词长度数组,表示每一个单词的长度
/*
found函数
stp选择的上一个单词下标,i为后一个,例子在后面
首先 进行for循环,循环的意义也就是从之前的单词最后一个开始和
后面的单词从前向后比较。
所以总结一下循环的意义
1.在dp中确定长度l和范围
2.在字符串搜索中是两个单词比较,可参照kmp算法。
如果(if)在for循环中的q与后面单词的第一个字母相等,那就好办了,直接往后搜索。
做判断用的。
然后定义一个相同位数,后面dfs说过。从q开始向前一个单词最后一个字母前进,
后面的单词从第一个字母向后面前进,一一比较。
比较不过的内层for循环就break。注意!一定要比到上一个字符的最后一个字符答案才有效
因为要从 最后一个字母接嘛。然后返回x。
*/
int found(int stp,int i){ //stp代表上一个已经选择的单词下标 i表示当前的单词下标
int q,w,j;
for(q=len[stp]-1;q>=1;q--){ //为使长度最长,从上一个单词的末尾开始搜索
if(s[stp].lin[q]==s[i].lin[0]){ //第一个字符匹配后继续看后面还有多少位
int x=0; //相同位数
for(w=q,j=0;w<len[stp]&&j<len[i];w++,j++){
if(s[stp].lin[w]==s[i].lin[j])x++;
else break;
}
if(w==len[stp])return x;//注意!一定要比到上一个字符的最后一个字符答案才有效
}
}
return 0;
}
/*
dfs函数
dfs函数的状态就是(i,length),也就是已知量,目的是求输出
以此字母开头的最长的“龙”的长度。
stp指的是上一个已经选择的单词,也就是“i”
*/
void dfs(int stp,int length){ //上一个已经选择的单词 当前的最长单词长度
int i;
bool mark=false;//以旗子来判断,是否找到
for(i=0;i<t;i++){
if(vis[i]>2)continue;//单词已经被找过两次以上,跳过
int x=found(stp,i); //x代表上一个单词与当前单词的匹配位数
if(x!=0){ //匹配位数不为0就继续进行下一次的单词搜索
mark=true;
length+=(len[i]-x); //当前长度增加
//举个例子,比如length和theacter,这两个单词有th这两个字母
//重合的,所以结合就是lengtheacter,所以length+=(len[i]-x);
vis[i]+=1; //当前单词用过一个加一
dfs(i,length);//然后i就变成了“上一个”继续去找了。
//上一步递归不符合题意返回至此,长度减去不符合题意的单词,单词的使用次数减一,继续选择单词
length-=(len[i]-x); //回溯法原理。
vis[i]-=1;
}
}
if(mark==false){ //如果已经找不到单词,比较之前已经得到过的答案选择最长的单词数
ans=max(ans,length);
}
}
int main(){
int i;
char l1;
cin>>t;
for(i=0;i<t;i++){
cin>>s[i].lin;
len[i]=strlen(s[i].lin);
}
cin>>l1;
ans=0;
memset(vis,0,sizeof(vis)); //初始化单词标记数组
for(i=0;i<t;i++)if(s[i].lin[0]==l1){
vis[i]++; //每个单词使用一次,一个一个来。
dfs(i,len[i]);//当前使用的单词下标与长度
}
cout<<ans;//输出结果
return 0;
}
看法都在注释里面