#include <stdio.h> #include <memory.h> int cmpTimes( char* p, char* q ) { char* s = p; while( *s == *q && *s ){ ++s; ++q; } return ( s - p + 1 ) * 2 - ( (*s + *q) ? 1 : 0 ); } char str[4000][1001]; int mat[4000][4000]; // mat[i][j]代表i串和j串的比较次数 int likeleft[4000]; // leftlike[i] 代表在i串之前最和i串相近的字符串索引 int main() { int i, j, k, N, l, m, n, s; long long t; // 存放总比较次数 for( i = 1; scanf("%d%*c", &N), N; i++ ) { memset( &mat[0][0], 0, 4000*4000*sizeof(int) ); memset( likeleft, 0, 4000*sizeof(int) ); t = 0; gets( str[0] ); // 读第一个串 for( j = 1; j < N; j++ ) { gets( str[j] ); likeleft[j] = 0; // 对于每一个新读入的串,它的likeleft初始化为0 mat[0][j] = cmpTimes( str[0], str[j] ); t += mat[0][j]; // 和第一个串比较 for( k = 1; k < j; k++ ) { l = likeleft[k]; m = mat[l][j]; // l串 是和 k串最像的,那么就用j串和l串的关系推导出j串和k串的关系 n = mat[l][k]; if( m != n ) mat[k][j] = m < n ? m : n; // j 串和 k串不同的位置已知 else if( m & 1 ){ // 开始的m次比较不需要,可以直接从后面不一样的开始比较 s = ( m - 1 )>>1; mat[k][j] = m - 1 + cmpTimes( str[k] + s, str[j] + s ); } else mat[k][j] = m; // m==n且m%2==0, 说明 l,k,j串完全一样 mat[likeleft[j]][j] < mat[k][j] && ( likeleft[j] = k ); // 更新j串的likeleft t += mat[k][j]; // 累加比较次数 } } printf("Case %d: %lld/n", i, t ); } return 0; } // Uva 11732 /* int strcmp(char *s, char *t) { int i; for (i=0; s[i]==t[i]; i++) //比较1 if (s[i]=='/0') // 比较2 return 0; return s[i] - t[i]; } */ // 字符串比较次数就是<比较1>和<比较2>次数的累加。 // 给N个字符串,两两相比,问总共的比较次数。 // // 如果直接两两算,那么时间复杂度在最坏情况下是O(l*n*n). // 考虑到上一次的比较结果可以为下一次所用,那么问题就迎刃而解了。 // 用邻接矩阵表示比较次数,另外用一个数组存放和此串最相近的(likeleft)且位于前面的串的下标 // 那么在计算此串的时候就可以用likeleft作为参考,因为那个串是和此串最像的串。