预处理+DFS【洛谷P1019】

照例先上题目链接:https://www.luogu.org/problemnew/show/P1019

一拿到题是懵的,读了好几遍题目才确定题意,想了半天都在想怎么样才能模拟把字符串输出出来。果然对于这种含有模拟的题目我还是很不拿手。

由于对这个题目毫无思路,在队友@TDD的启(讲)发(解)下,我才勉强对这个题目有了新的认识。

这题根本就不需要把字符输出出来啊!


下面是思路:

首先题目要求,求出最长的字符串,那么我们就需要找到每两个单词之间最短的重合长度(最小重叠部分),

举个例子,abcd和dddddd,他们合并之后是abcddddddd,他们的最小重叠长度是1(我也不知道对不对,如果不对的话请给我留言,谢谢大家啦)

题目还给出了一个首字符,那么我们就直接根据首字符进行暴力DFS,如果可以接龙,那么就直接把合并上的长度加上去,直到所有的字符都不能合并为止。每一次DFS都要对于长度求一个max。

这就是大体思路,但是我们注意到,DFS里面需要找很多次单词的接龙单词,这咋找啊!我哪知道这个单词后面能接哪一个单词啊!所以我们就需要对所有的字符串进行【预处理】,func(i,j)表示第i个字符串后面接第j个字符串的最小重叠长度。这样预处理完了之后,就可以愉快的DFS辽。

下面上代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 30;
int num[maxn][maxn];
int used[maxn];
int ans = 0;
int tmp = 0;
int n;
void init()
{
	memset(num,0,sizeof(num));
	memset(used,0,sizeof(used));
}
string str[maxn];
int func(int a,int b)
{
	for(int i=str[a].size()-1;i>=0;i--)
	{
		int pb = 0;
		int j;
		for(j=i;j<str[a].size();j++)
		{
			if(str[a][j]==str[b][pb] && pb<str[b].size())
			{
				pb++;
			}
			else
			{
				break;
			}
		}
		if(j>=str[a].size())
		{
			return str[a].size()-i;
		}
	}
	return -1;
}
void dfs(int s)
{
	bool found = false;
	for(int i=0;i<n;i++)
	{
		if(used[i]<2 && num[s][i]!=-1 && num[s][i]!=str[i].size() && num[s][i]!=str[s].size())
		{
			found = true;
			tmp += str[i].size()-num[s][i];
			used[i]++;
			dfs(i);
			tmp -= str[i].size()-num[s][i];
			used[i]--;
		}
	}
	if(!found)
	{
		ans = max(ans,tmp);
	}
}
int main()
{
	while(cin>>n)
	{
		for(int i=0;i<n;i++)
		{
			cin>>str[i];
		}
		cin>>str[n];
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				num[i][j] = func(i,j);
			}
		}
		for(int i=0;i<n;i++)
		{
			if(str[i][0]==str[n][0])
			{
				used[i]++;
				tmp = str[i].size();
				dfs(i);
				used[i]--;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
} 

总结:感觉这个题目不仅思路很混乱,而且代码实现也比较复杂。果然还是自己太蒻了!

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/83115649