POJ 1699

算是一道水題,但是被折騰並不幸多次貢獻WA...

太久不寫代碼略顯生疏。思路其實是有的,大體分爲兩種

第一種是開始嘗試的,但就是這裏開始不停找不到出路,不明白是何方測評神仙數據卡着,discuss裏的有問題的測試數據過的都沒問題。思路如下:

  • 之前一道題的練習,領悟到AC自動機本質上是構建了一個很特殊的圖,我們尋找這樣的字符串的過程,其實可以看做在這個圖上旅行的過程,找到一個最短路徑,遍歷所有點,是的,就變成一個數量級較小的旅行商問題
  • 問題在於重疊的定義,這道題是必須前綴和後綴連接的。所以每當發現當前節點是一個完整匹配串的節點的時候,需要不斷利用fail指針回溯找到當前此狀態所有可以被自動機完整接受的合法輸入串。
  • 這道題妙就妙在雖然是旅行商,但是可以利用狀態壓縮DP來做,或者狀態壓縮BFS,只是一直沒能調出來,不知道那個神仙數據是什麼

第二種其實是一開始方法實踐之後想到的,其實方法一繞了遠路,與其用AC自動機完整求出,不如直接構造兩兩輸入字符串的“距離”矩陣:

  • 所以思路很明顯,先構造距離矩陣
  • 雖然這道題數據量很小,但還是值得思考,尋找有沒有快速方法
  • 自己有想過KMP但沒實現出來,簡單看了下便恍然大悟,非常巧妙的思路,KMP匹配(s在前,t在後),將,s作爲目標串,而t作爲模式串,就這樣一直匹配到s的最後,這是剩餘的在t中用作索引的變量就記錄了這個長度值,按照一般KMP匹配思路,其實這是一個邊角料而已,所以靈活運用就體現在這。
  • 尋路又用到巧妙的狀壓DP,定義dp(i, j)爲在狀態i,且最後一個串爲j(順序以輸入爲準)
#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cmath>
#include <string>
#include <stack>
#include <map>
#include <set>
#include <deque>
using namespace std;

const int maxn= 15;
const int maxl= 25;
const int maxs= (1<<10)+5;
const int INF= 0x3f3f3f3f;

int n;
char dna[maxn][maxl];
int bd[maxn][maxn], k_next[maxn][maxl];
int lth[maxn];
int dv[maxs][maxn];

void PreKMP(char *x, int m, int *kmp_next)
{
	int i, j;
	i= 0;
	j= kmp_next[0]= -1;

	while (i< m){
		while (j> -1 && x[i]!= x[j]){
			j= kmp_next[j];
		}

		if (x[++i]== x[++j]){
			kmp_next[i]= kmp_next[j];
		}
		else{
			kmp_next[i]= j;
		}
	}
}
int CalcOver(int s, int t)
{
	int i, j;
	i= j= 0;
	while (1){
		while (j> -1 && dna[s][i]!= dna[t][j]){
			j= k_next[t][j];
		}
		++i;
		++j;
		if (i>= lth[s]){
			break;
		}
		if (j>= lth[t]){
			j= k_next[t][j];
		}
	}

	return j;
}

int main(int argc, char const *argv[])
{
	int kase= 0;
	scanf("%d", &kase);

	while (kase--){
		scanf("%d", &n);
		for (int i= 0; i< n; ++i){
			scanf(" %s", dna[i]);
			lth[i]= strlen(dna[i]);
			PreKMP(dna[i], lth[i], k_next[i]);
		}

		for (int i= 0; i< n; ++i){
			for (int j= 0; j< n; ++j){
				if (i== j){
					bd[i][j]= lth[i];
				}
				else{
					bd[i][j]= CalcOver(i, j);
				}
			}
		}

		const int e_mk= (1<<n)-1;
		for (int i= 0; i<= e_mk; ++i){
			for (int j= 0; j< n; ++j){
				dv[i][j]= INF;
				if (i & (1<<j)){
					if (0== (i ^ (1<<j))){
						dv[i][j]= lth[j];
					}
					else{
						int ii= i ^ (1<<j);
						for (int k= 0; k< n; ++k){
							if (ii & (1<<k)){
								dv[i][j]= min(dv[i][j], dv[ii][k]+lth[j]-bd[k][j]);
							}
						}
					}
				}
			}
		}
		int ans= INF;
		for (int i= 0; i< n; ++i){
			ans= min(ans, dv[e_mk][i]);
		}

		printf("%d\n", ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章