正难则反,考虑后涂色的不会被先涂色的覆盖,因此可以到序求解。

具体的,先找出输入矩阵中字典序最大的字母,从这个字母开始倒序枚举。

对于每一个字母,有出现过和没有出现过两种情况,如果没有出现过,直接在一个比它大的位置涂色一格就可以了(这一格将被覆盖,等于没涂),实现时可以直接填充为最后一个字母的第一格。如果出现过,判断它是否合法:首先判断出这个字母出现的所有位置是否都在同一行或同一列,再判断出现顺序是否连续(注意到如果之后有字母占有一个位置,那么它就不影响连续性,可以先涂上再覆盖),如果不合法直接输出无解,否则存入答案数组。

最后将答案数组倒序输出就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct point{
int x,y;
point(){}
point(int xx,int yy){
x=xx,y=yy;
}
friend bool operator < (point __,point _){
// return __.x+__.y<_.x+_.y;
return __.x==_.x?__.y<_.y:__.x<_.x;
}
};
struct answer{
point p1,p2;
};
vector<point>p[200];
map<point,bool>vis;
int n,m,sum;
char c,mx;
vector<answer>ans;
bool check(char ch){
if(p[ch].size()==1){
return true;
}
point s=p[ch][0];
point e=p[ch][p[ch].size()-1];
int po=0;
if(s.x==e.x){
for(auto i:p[ch]){
if(i.x!=s.x){
return false;
}
}
for(int i=s.y;i<=e.y;i++){
if(i==p[ch][po].y){
po++;
}
else if(!vis[point(s.x,i)]){
return false;
}
}
}
else if(s.y==e.y){
for(auto i:p[ch]){
if(i.y!=s.y){
return false;
}
}
for(int i=s.x;i<=e.x;i++){
if(i==p[ch][po].x){
po++;
}
else if(!vis[point(i,s.y)]){
return false;
}
}
}
else{
return false;
}
return true;
}
void solve(){
for(int i='a';i<='z';i++){
p[i].clear();
}
vis.clear();
ans.clear();
sum=0,mx='a';
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>c;
if(c!='.'){
p[c].push_back({i,j});
sum++;
mx=max(mx,c);
}
}
}
if(sum==0){
cout<<"YES\n0\n";
return;
}
for(int i=mx;i>='a';i--){
if(p[i].size()==0){
ans.push_back({p[mx][0],p[mx][0]});
continue;
}
if(!check(i)){
cout<<"NO"<<endl;
return;
}
ans.push_back({p[i][0],p[i][p[i].size()-1]});
for(auto j:p[i]){
vis[j]=1;
}
}
cout<<"YES"<<endl;
cout<<ans.size()<<endl;
for(int i=ans.size()-1;i>=0;i--){
cout<<ans[i].p1.x<<" "<<ans[i].p1.y<<" "<<ans[i].p2.x<<" "<<ans[i].p2.y<<endl;
}
}
signed main(){
#ifdef USE_FILE_IO
freopen("code.in","r",stdin);
cerr<<"[File IO]"<<endl;
#endif
ios::sync_with_stdio(0);
cin.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}