题意是,给出一个n * m的字母矩阵,从(1, 1)点走到(n, m)点,经过路径的上的每一个字母按顺序会排成一个字符串。现在希望排成的字符串是回文串,问这样的路径会有多少条,结果对1e9 + 7取模。
这是一个对官方题解从开始到放弃的过程,这场的上一题感觉官解写得短小精悍,然后这一题,是不是短小精悍不知道,反正是真看不下去。看了这篇博客懂了不少。
https://blog.csdn.net/u010660276/article/details/47700441
大的思路看这个就很棒了,补充一下我在理解上的一点障碍。虽然是只存了两个点的横坐标,但是由于step的限制,其实dp数组是连带着纵坐标一起存下来的,这个意识一定要弄清楚,不然会连带着想不清为什么可以这么递推、为什么不会多出来答案。可以在理解思路的时候把这个dp数组想象成是有纵坐标的,然后递推过来就会清晰一些。另外,这篇题解里面代码的递推部分注释感觉有问题,向哪里向哪里不太好理解,可以忽略它的注释。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 550;
const int _n = 1e9 +7;
int n, m;
char s[mx][mx];
ll dp[2][mx][mx];//上一个状态or当前状态 从(1, 1)开始的点的横坐标 (n, m)开始的点的横坐标
inline ll add(ll a, ll b, ll c, ll d){
return (a + b + c + d) % _n;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++){
scanf("%s", s[i] + 1);
}
int cur = 0;
dp[cur][1][n] = (s[1][1] == s[n][m]);
for(int step = 1; step <= ((n + m - 2) >> 1); step++){
int lst = cur;
cur ^= 1;
for(int x1 = 1; x1 <= n; x1++)
for(int x2 = 1; x2 <= n; x2++){
dp[cur][x1][x2] = 0;
}
for(int x1 = 1; x1 <= n && x1 <= 1 + step; x1++)
for(int x2 = n; x2 >= 1 && x2 >= n - step; x2--){
int y1 = 1 + step - (x1 - 1);
int y2 = n - x2 + m - step;
if(s[x1][y1] != s[x2][y2]) continue;
dp[cur][x1][x2] = add(dp[lst][x1][x2], dp[lst][x1][x2+1],
dp[lst][x1-1][x2], dp[lst][x1-1][x2+1]);
}
}
ll ans = 0;
for(int i = 1; i <= n; i++){
ans += dp[cur][i][i];
ans %= _n;
}
if((m + n) & 1){
for(int i = 1; i <= n; i++){
ans += dp[cur][i][i+1];
ans %= _n;
}
}
cout << ans << endl;
return 0;
}