tc的题意还是非常好理解的,所以也就不再赘述题意。
首先,对于这道题,我第一个想的就是组合数公式,首先对所有的字符串进行字典序排序,这样的话,所有的前缀连通块就全都凑到了一起,然后数出来所有的连通块的长度,按照组合数公式往里面套,就可以了,但是组合数取模还是忘了= =于是乎就没有按着这个方法写。
另一个方法貌似是dp,说实话,最开始并没有想到dp的这个姿势(果然还是dp太渣渣了)。
dp[i][j]表示的是以第i个字符串为结尾,前缀连续长度为j的方案数,当然我们目前只考虑这个前缀的规律,因为剩下的随意,直接乘以阶乘就可以啦~~那么这个数一定来自于以第i个字符串的所有的前缀为结尾,前缀连续长度为j-1个的时候的方案数,那么我们只需要预处理出来每一个字符串的前缀是第几个,就可以得到这个方法了。
代码如下
class SimilarNames2 {
public:
typedef long long ll;
ll a[55], dp[55][55];
vector<int> adj[55];
void init(){
memset(a, 0, sizeof(a));
a[0] = 1;
for (int i = 1; i <= 50; i++)
a[i] = (a[i - 1] * i) % mod;
}
bool judge(string a, string b){
return b.find(a) == 0;
}
int count(vector <string> names, int L){
init();
int n = names.size();
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (i == j) continue;
else if (judge(names[i], names[j])) adj[j].push_back(i);
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++) dp[i][0] = 1;
for (int j = 1; j < L; j++)
for (int i = 0; i < n; i++)
for (int k = 0; k < adj[i].size(); k++){
int x = adj[i][k];
dp[i][j] = (dp[i][j] + dp[x][j - 1]) % mod;
}
ll ans = 0;
for (int i = 0; i < n; i++)
ans = (ans + dp[i][L - 1] * a[n - L]) % mod;
return (int)ans;
}
};