这道题目是在实验室和队友插科打诨yy出来的,当初的心态已经完全记不起来了。想到解法的时候很高兴,然后?没有然后了。
题意是:给出一个n要求用前n个英文大写字母组成符合要求的最长字符串,求这样的字符串个数mod 1,000,000,007。以下为3个要求
1.不能连续出现两个相同字母如ABBA ---->B连续出现
2.不能出现重复序列如TAETBE --->TETE这样的序列是不允许的
3.是一个2的扩展ABADAEAF ---->AAAA这样的子序列也是不允许的
首先我们可以注意到 这样的字符串长度是有固定规律的。是2*n-1 然后在考虑这样的字符串跟它每一位上的字母是有关系的么?答案是没有。为什么呢?因为这样的解绝对是轮换对称的。换而言之,对于一个这样的解你把A换成B,B换成C...类似的变换都一样可以再创造出来一组可行解,但是他们是等价的。
因此我们现在思考,我们改如何求出这样的等价的可行解的组数?如果不是字母的话是什么决定了这些解?答案是:等量关系。这里决定的并不是数值,而是这些数值之间的关系。我们把相等的字母用弧线连接起来。如下图:
对于第一种解我们认为 ABCBA ACBCA BACAB BCACB CABAC CBABC 是等价的
对于第二种解我们认为 ABACA ACABA BABCA BCBAB CBCAC CACBC 是等价的
因为我们知道一个串中不会出现大于等于4个相同字符。在考虑有没有可能出现A...A...A...的情况呢?结果是不能,因为很容易证明这样的安排是不可能达到最长的串的条件的。假设以下这种情况:A...A...AX 因为有X的出现因此之前的两段之中是不可以出现X的,因此X只能出现1次但是假如把X放到之前的两段中间X可以出现2次,即A...X...A...X...A因此不能出现A...A...A...的情况。
于是我们把一个连接到第一个字母的连线叫做一个桥。那么所有符合条件的字符串只有一个桥或者两个桥。 (图中的第一个例子是一个桥,第二个例子是两个桥)然后开始使用dp,把dp[i][1]视为采用i个字母的且仅有一个桥的情况下的解的个数,dp[i][2]为有两个桥的解的个数。状态确定之后我们来确定转移关系。
所有i个字母的一个桥的解和两个桥的解在左右两端加上新的字符之后可以获得i+1个字母的一个桥解
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
class FarStrings {
public:
int n;
int mi(int x1,int x2){
if (x1<x2) return (x1); return (x2);
}
int cal(string s,string t){
int dp[30][30];
s+='*';
t+='*';
for (int c_i=0;c_i<=n;c_i++)
for (int c_j=0;c_j<=n;c_j++)
if (c_i==0 && c_j==0)
dp[c_i][c_j]=0;
else
dp[c_i][c_j]=65536;
for (int c_i=0;c_i<=n;c_i++)
for (int c_j=0;c_j<=n;c_j++)
if (t[c_i]==s[c_j])
dp[c_i+1][c_j+1]=mi(dp[c_i+1][c_j+1],dp[c_i][c_j]);
else{
dp[c_i+1][c_j+1]=mi(dp[c_i+1][c_j+1],dp[c_i][c_j]+1);
dp[c_i+1][c_j]=mi(dp[c_i+1][c_j],dp[c_i][c_j]+1);
dp[c_i][c_j+1]=mi(dp[c_i][c_j+1],dp[c_i][c_j]+1);
}
return (dp[n][n]);
}
bool poss(int d,string t,string s,int pos){
int maxn=cal(s,t);
for (int p_i=pos+1;p_i<n;p_i++)
s[p_i]=t[p_i];
int minn=cal(s,t);
if (minn<=d && d<=maxn)
return (true);
return (false);
}
vector <string> find(string t) {
n=t.size();
vector<string> ans;
ans.clear();
for (int i=0;i<n;i++){
string s(n,'?');
for (int j=0;j<n;j++){
s[j]='a';
while (!poss(i+1,t,s,j))
s[j]++;
}
ans.push_back(s);
}
return (ans);
}
};
两个长度不同的一桥解可以拼接为一个两桥解。至此转移完成
以下为代码