Description
有n个字符串,给这些字符串分组,使得每个字符串属于且仅属于一个组。
对于一个合法的分组,至少满足以下两个条件种的一个:
- 所有字符串的k前缀相同(即前k个字母相同)
- 所有字符串的k后缀相同(即后k个字母相同)
你需要给这些字符串分组,使得所分的组数最少。
50%的数据n<=100
100%的数据n<=5000,k<=550
Solution
这道题好劲啊,没有想法
可以想到转成图上的问题来做,把字符串看成边,前后缀看做点,哈希一波处理标号。把所有匹配边和匹配点求出来,然后对于每个连通块讨论:若该连通块没有未匹配点,则把该连通块某一侧的点全部选掉即可。否则就从任意一个未匹配点开始,搜索出一棵交替树(未匹配边->匹配边->未匹配边…),那么交替树上深度为偶数的点就都可以选掉。显然每个点对应一条匹配边,如果某条匹配边两端都是奇数点,则会出现奇环,与二分图矛盾。
但考虑到一棵交替树未必能找到一个连通块的所有点,那么每找到一棵交替树,就把树上的点删掉,再继续重复就好了。这样的话就可以保证每一步都是正确的。
Code
#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>
#include <map>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
typedef long long ll;
const int MOD=1000000007;
const int N=50005;
const int E=500005;
struct edge{int x,y,used,next;}e[E];
std:: vector<int> vec[N];
std:: vector<int>:: iterator it;
std:: map<int,int> suf,pre;
int link[N],chosen[N],vis[N];
int prt[N],left[N];
int ls[N],edCnt=1,cnt=0;
char str[N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void addEdge(int x,int y) {
e[++edCnt]=(edge){x,y,0,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge){y,x,0,ls[y]}; ls[y]=edCnt;
}
int find(int now,int id) {
for (int i=ls[now];i;i=e[i].next) {
if (vis[e[i].y]==id) continue;
vis[e[i].y]=id;
if (!link[e[i].y]||find(link[e[i].y],id)) {
link[e[i].y]=now;
return 1;
}
}
return 0;
}
int hungary() {
int ret=0;
rep(i,1,cnt) if (left[i]) ret+=find(i,i);
return ret;
}
void print(int now) {
int size=0;
for (it=vec[now].begin();it!=vec[now].end();it++) {
if (!prt[*it]) size++;
}
if (!size) return ;
printf("%d ", size);
for (it=vec[now].begin();it!=vec[now].end();it++) {
if (!prt[*it]) {
printf("%d ", *it);
prt[*it]=1;
}
}
puts("");
}
void get_ans(int now,int d) {
if (d) print(now);
vis[now]=1;
for (int i=ls[now];i;i=e[i].next) {
if (e[i].used==d&&!vis[e[i].y]) {
get_ans(e[i].y,d^1);
}
}
}
void solve() {
for (int i=2;i<=edCnt;i+=2) {
if (link[e[i].x]==e[i].y||link[e[i].y]==e[i].x) {
e[i].used=e[i^1].used=1;
chosen[e[i].x]=chosen[e[i].y]=1;
}
}
fill(vis,0);
rep(i,1,cnt) {
if (!vis[i]&&!chosen[i]) get_ans(i,0);
}
rep(i,1,cnt) {
if (!vis[i]&&chosen[i]&&left[i]) print(i);
}
}
void init() {
int n=read(),k=read();
rep(i,1,n) {
scanf("%s",&str);
int x=0,y=0,len=strlen(str);
rep(j,0,k-1) {
x=(ll)(x*27+str[j]-'A')%MOD;
y=(ll)(y*27+str[len-j-1]-'A')%MOD;
}
if (!pre[x]) pre[x]=++cnt;
if (!suf[y]) suf[y]=++cnt;
addEdge(pre[x],suf[y]);
vec[pre[x]].push_back(i);
vec[suf[y]].push_back(i);
left[pre[x]]=1;
// printf("%d %d\n", pre[x],suf[y]);
}
}
int main(void) {
// freopen("data.in","r",stdin);
// freopen("myp.out","w",stdout);
init();
printf("%d\n", hungary());
solve();
return 0;
}