题意
给定一个字符串,字符集大小为8
将每个位置当做一个点,相邻位置有长度为1的连边,任意相同字母的位置之间有长度为1的连边。求图的直径和直径的长度。
n≤105
题解
求图的直径需要求任意两点间的最短路,这是不现实的。
设
DIS(i,j)
为位置
i
与位置
其中 dis(i,c) 表示从某个位置到达字符 x 的最短距离。
为什么
同时我们能得到这样的结论,树的直径小于字符集大小的二倍,即15,这个串就是一个例子aabbccddeeffgghh
枚举
i
,同时枚举
- |i−j|<16 ,此时直接由 DIS(i,j)=min{|i−j|,min{dis(i,c)+1+dis(j,c)}} 求出
-
|i−j|≥16
,不能直接枚举
j
了,此时
DIS(i,j)=min{dis(i,c)+1+dis(j,c)}
现在设法求出
|i−j|≥16
时的
max{DIS(i,j)}
,注意此时的
i
是固定的,
再设
d(c1,c2)
代表字符
c1
与
c2
之间的最短距离。那么
d(sj,c)≤dis(j,c)≤d(sj,c)+1
,也就是要么
sj
与
c
之间最短路恰好是
预处理求出
再设数组
cnt(x,mask)
来统计
j(|i−j|≥16),sj=x且mask(j)=mask
的数量。
i
确定时先确定
这样就能求出 i,sj,mask 确定时的最短路并更新答案。
时间复杂度 O(n⋅|C|3+n⋅|C|2⋅2|C|) ,其中 |C| 为字符集大小。
代码
/// by ztx
/// blog.csdn.net/hzoi_ztx
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v) for(i=v.begin();i!=v.end();i++)
#define r(x) read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
if (CH == '-') NEG = true , CH = getchar() ;
while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
if (NEG) ret = -ret ;
}
#define maxn 100010LL
#define infi 0x3f3f3f3fLL
#define id(c) (c+n+1)
int n, ans;
ll cnt;
int dis[maxn][8], d[8][8], mask[maxn],c[8][256];
char s[maxn];
std::queue<int> q;
inline void bfs(int x) {
int i, u, v;
dis[id(x)][x] = 0;
Rep (i,1,n) if (s[i] == x)
dis[i][x] = 0, q.push(i);
while (!q.empty())
if (u = q.front(), q.pop(), u <= n) {
if (v = u+1, 1<=v && v<=n && dis[v][x]==infi) {
dis[v][x] = dis[u][x]+1, q.push(v);
if (dis[id(s[v])][x] == infi)
dis[id(s[v])][x] = dis[u][x]+1, q.push(id(s[v]));
}
if (v = u-1, 1<=v && v<=n && dis[v][x]==infi) {
dis[v][x] = dis[u][x]+1, q.push(v);
if (dis[id(s[v])][x] == infi)
dis[id(s[v])][x] = dis[u][x]+1, q.push(id(s[v]));
}
}
else Rep (i,1,n) if (id(s[i])==u && dis[i][x]==infi)
dis[i][x] = dis[u][x]+1, q.push(i);
}
int main() {
int i, j, k, l, now, x;
r(n), scanf("%s", s+1);
Rep (i,1,n) s[i] -= 'a';
memset(dis, 0x3f, sizeof dis);
while (!q.empty()) q.pop();
rep (i,0,8) {
bfs(i);
rep (x,0,8) d[x][i] = dis[id(x)][i];
}
Rep (i,1,n) rep (x,0,8)
if (dis[i][x] > d[s[i]][x]) mask[i] |= 1<<x;
Rep (i,1,n) {
rep (j,std::max(i-15,1),i) {
now = i-j;
rep (k,0,8) now = std::min(now, dis[j][k]+dis[i][k]+1);
if (now == ans) cnt ++ ;
if (now > ans) ans = now, cnt = 1;
}
int t = i-16;
if (t >= 1) c[s[t]][mask[t]] ++ ;
rep (x,0,8) rep (k,0,256)
if (c[x][k]) {
now = infi;
rep (l,0,8) now = std::min(now, d[x][l]+1+dis[i][l]+(k&(1<<l)));
if (now == ans) cnt += c[x][k];
if (now > ans) ans = now, cnt = c[x][k];
}
}
printf("%d %lld\n", ans, cnt);
END: getchar(), getchar();
return 0;
}