1. 程式人生 > >預處理+DFS【洛谷P1019】

預處理+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;
} 

總結:感覺這個題目不僅思路很混亂,而且程式碼實現也比較複雜。果然還是自己太蒻了!