解法
第一步,分别统计以(i,j)
结尾的,长度不超过k
的单词总长度,横着和竖着分别记作h[i,j,k]
和v[i,j,k]
然后我们考虑DP,状态为f[i1,j1,i2,j2]
四重循环遍历的时候:
-
i2
每增加1的时候,总是从f[i1,j1,i2,j1]
开始的,这是竖着的一列,所以我们需要知道这一列里:- 横着,以区间
(i1,j1)
到(i2,j1)
之间的字符结尾,长度不超过1的单词总长度,假设这部分为A
- 竖着,在区间
(i1,j1)
到(i2,j1)
之间的单词总长度,假设这部分为B
那么有初始条件:
f[i1,j1,i2,j1]=A+B
- 横着,以区间
-
当
i2
固定,j2
每增加1的时候,相当于与f[i1,j1,i2,j1-1]
相比增加了竖着的一列,与上一条相似地,我们需要增加:- 横着,以区间
(i1,j2)
到(i2,j2)
之间的字符结尾,长度不超过j2-j1+1
的单词总长度,假设这部分为C
- 竖着,在区间
(i1,j2)
到(i2,j2)
之间的单词总长度,假设这部分为D
那么递推方程为:
f[i1,j1,i2,j2]=f[i1,j1,i2,j2-1]+C+D
- 横着,以区间
如何求第一部分呢?我们可以对h
数组,在固定j,k
的情况下,对i
轴求前缀和,记作cumv[i,j,k]=cumv[i-1,j,k]+h[i,j,k]
,这样就有:
A = cumv[i2,j1,1]-cumv[i1-1,j1,1]
C = cumv[i2,j2,j2-j1+1]-cumv[i1-1,j2,j2-j1+1]
对于第二部分,我们可以用cumh[i,j,k]
记录区间(i-k+1,j)
到(i,j)
之间的单词总长度,它可以这么求:
cumh[i,j,k] = cumh[i-1,j,k-1]+v[i,j,k]
这样就有:
B = cumh[i2,j1,i2-i1+1]
D = cumh[i2,j2,i2-i1+1]
注意所有的数组都用int
会超内存限制,我们可以分析一下:
- 数组
v
和h
最多是1000个单词长度为100,甚至题目还有条件限制所有单词长度加起来不超过5000,所以它们的值最大也不会超过10**4
(假如正逆要算两遍的话) - 然后
cumv
和cumh
相当于是对某一维进行累加,所以多100倍,即10**6
- 然后是
f
,又进行了累加,所以大约再涨100倍,即10**8
可见3个都不会超过int
,但是,在比较分数大小的时候,如果像我一样用的是乘法而不是除法,那么中间结果有可能超过int
,需要转换成long long
#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <unordered_set>
#include <unordered_map>
#include <cmath>
#include <vector>
#include <algorithm>
#define MAXN 110
#define NINF 0x80000000
using namespace std;
typedef long long lld;
typedef unordered_multiset<string> DICT;
string matrix[MAXN];
unordered_map<int,DICT> words;
int v[MAXN][MAXN][MAXN],h[MAXN][MAXN][MAXN];
int cumv[MAXN][MAXN][MAXN],cumh[MAXN][MAXN][MAXN];
int f[MAXN][MAXN][MAXN][MAXN];
int r,c,w;
lld score,size,cnt;
void insert(string &word) {
int l = word.size();
if(words.find(l)==words.end())
words[l] = DICT();
words[l].insert(word);
}
void reduce(lld &a,lld &b) {
lld x=a,y=b;
if(x<y) {
x^=y;y^=x;y^=x;
}
while(x%y!=0) {
int tmp = y;
y = x%y;
x = tmp;
}
a /= y,b/=y;
}
void newVal(int i1,int j1,int i2,int j2) {
lld s = i2-i1+j2-j1+2;
lld sc = f[i1][j1][i2][j2];
if(sc*size>=score*s) {
if(sc*size==score*s) cnt++;
else {
cnt = 1;
score = f[i1][j1][i2][j2];
size = s;
reduce(score,size);
}
}
}
void solve() {
score = 0, size = 1, cnt = 0;
memset(v,0,sizeof(int)*MAXN*MAXN*MAXN);
memset(h,0,sizeof(int)*MAXN*MAXN*MAXN);
memset(cumv,0,sizeof(int)*MAXN*MAXN*MAXN);
memset(cumh,0,sizeof(int)*MAXN*MAXN*MAXN);
for(int i=1;i<=r;++i)
for(int j=0;j<c;++j) {
string ver="";
for(int k=1;k<=i;++k) {
v[i][j][k] = v[i][j][k-1];
ver += matrix[i-k+1][j];
if(words.find(k)!=words.end()) {
DICT &dict = words[k];
v[i][j][k]+=dict.count(ver)*k;
}
}
string hor="";
for(int k=1;k<=1+j;++k) {
h[i][j][k] = h[i][j][k-1];
hor += matrix[i][j-k+1];
if(words.find(k)!=words.end()) {
DICT &dict = words[k];
h[i][j][k]+=dict.count(hor)*k;
}
}
}
for(int i=1;i<=r;++i)
for(int j=0;j<c;++j) {
for(int k=1;k<=1+j;++k) {
cumv[i][j][k] = cumv[i-1][j][k]+h[i][j][k];
}
for(int k=1;k<=i;++k) {
cumh[i][j][k] = cumh[i-1][j][k-1]+v[i][j][k];
}
}
memset(f,0,sizeof(int)*MAXN*MAXN*MAXN*MAXN);
for(int i1=1;i1<=r;++i1)
for(int j1=0;j1<c;++j1) {
for(int i2=i1;i2<=r;++i2) {
f[i1][j1][i2][j1] = cumh[i2][j1][i2-i1+1]+cumv[i2][j1][1]-cumv[i1-1][j1][1];
newVal(i1,j1,i2,j1);
for(int j2=j1+1;j2<c;++j2) {
f[i1][j1][i2][j2] = f[i1][j1][i2][j2-1]+cumh[i2][j2][i2-i1+1]+cumv[i2][j2][j2-j1+1]-cumv[i1-1][j2][j2-j1+1];
newVal(i1,j1,i2,j2);
}
}
}
}
int main() {
// freopen("obj.txt","r",stdin);
// freopen("my.out","w",stdout);
int t;
scanf("%d",&t);
for(auto round=1;round<=t;++round) {
words.clear();
scanf("%d%d%d\n",&r,&c,&w);
for(int i=1;i<=r;++i)
getline(cin,matrix[i]);
string str;
for(int i=0;i<w;++i) {
getline(cin,str);
insert(str);
reverse(str.begin(),str.end());
insert(str);
}
solve();
reduce(score,size);
printf("Case #%d: %lld/%lld %lld\n",round,score,size,cnt);
}
}