POJ 2774 Long Long Message

来源:互联网 发布:反洗钱系统数据抓取 编辑:程序博客网 时间:2024/05/16 14:33

题目大意:

        小猫母亲最近生病,但是小猫家很穷没钱来回火车,因此只能在当地移动中心使用SMS发送消息给它母亲,但是最近移动中心的SMS系统故障,它的消息可能夹杂在其他消息或者乱码中(但是它的消息中不含有其他消息或乱码,即消息是完整的,只不过插在了其他消息或乱码当中),为确保母亲能受到有效的消息并能识别,他连续发送两次,这两次个产生一串字符序列,每条序列中都有小猫给母亲的信息(在这两个序列中的位置可能不同,两边的字符可能不同),现请找出小猫可能发送的最长信息的长度(即找出两字符串中的最长公共子序列)。

       现只有一个测例,测例中给出两字符串(长度不超过100,000),要求输出最长公共子序列长度。

题目链接

注释代码:

/*         * Problem ID : POJ 2774 Long Long Message * Author     : Lirx.t.Una         * Language   : C        * Run Time   : 594 ms         * Run Memory : 3524 KB        */#include <string.h>#include <stdio.h>//number of characters//字符的数量//a ~ z + # + '\0'//这也是基数排序中元素的初始权重//由于z最大,因此就以z作为最大初始化权重//这里是指ASCII码值#defineCHN'z'//字符串最大长度 × 2//最长公共连续子序列用后缀数组解时需要将两个两串字符合并//并且中间用一个特殊符号(#)隔开//因此主串的长度就是100,000 × 2 + 2(还包括#、'\0')#defineMAXLEN200002#defineMAX(x,y)( (x) > (y) ? (x) : (y) )#defineMIN(x,y)( (x) < (y) ? (x) : (y) )//buffer,临时缓冲区intbuf1[MAXLEN];intbuf2[MAXLEN];intbuf3[MAXLEN];//suffix array,后缀数组//sa[i]就表示排名为i的后缀的起始元素的下标intsa[MAXLEN];//height,height数组,表示以sa[i - 1]和sa[i]为开头的//后缀的最长公共前缀的长度inth[MAXLEN];//rank,基数排序的顺序位置数组//rank[i]就表示以下标为i的元素开头的后缀的排名int*rk;chars[MAXLEN];//string,存放字符串intkeq( int *v, int x, int y, int l ) {//key equal//倍增基数排序中比较两个位置的关键字是否相等//各个位置关键字有两个,一个是第一关键字还有一个是第二关键字//第一关键字就是v[x]和v[y],第二关键字是v[x + l]和v[y + l]//其中v是每个位置的值,x和y表示位置,l是combine length,即  //倍增基数排序的当前合并长度return v[x] == v[y] && v[x + l] == v[y + l];}voidbd_sa( char *s, int n, int mv ) {//build SA//使用倍增基数排序算法构造SA数组//string,原字符串//n为string中字符的个数,包括'\0',该元素rank始终为0,即排名最小的//maximum rank value,最大排名值//rank value of index i//s数组中i下标元素的排名值,初始化时就等于该位置字符的ASCII码大小//随着倍增基数排序的进行,该值(vi[i])会逐渐趋向于rank[i]//因此vi的长度是MAXLEN,而不是'z'int *vi;//sum of rank value/number of rank value//sv[i]表示排名值为i的元素的个数//是基数排序的核心部分//基数排序的核心思想就是利用排名值的个数构造rank数组,从而获得//各元素的排名而间接对数组排序int*sv;//location of rank value//lv[i]表示排名值为i的元素的下标位置int *lv;int*t;//temporary,临时指针变量,用于交换intv;//rank value,当前排名值intcl;//combine length,待排序的合并长度inti, j;//计数变量//初始化vi = buf1;sv = buf2;lv = buf3;//以下四行为初始基数排序//取名的时候利用了向量传递的特性方便记忆for ( i = 0; i <= mv; i++ ) sv[i] = 0;//计数器清零for ( i = 0; i <= n; i++ ) sv[ vi[i] = s[i] ]++;//初始化排名值并统计个数for ( i = 1; i <= mv; i++ ) sv[i] += sv[i - 1];//对排名值个数梯度累加for ( i = n; i >= 0; i-- ) sa[ --sv[ vi[i] ] ] = i;//间接得出排名sa//以上只完成了第一轮的第一关键字排名,一下就是倍增算法//每次先倍增地对第二关键字进行排名//然后再将第一和第二关键字进行合并并排名,然后就成为了下一轮的第一关键字//循环操作以上两步,该过程中v的值逐渐趋向于真实rank值,直到v的值完全等于rank值为止//最直接的表现就是v的最大值等于nfor ( cl = 1, v = 0; v <= n; cl <<= 1, mv = v ) {//由于上面四行代码完成了一个字符的排名//因此接下来的合并排序长度就为1//合并排序完了以后最短的合并后的单位长度就为2了,依次类推合并长度每过一轮就倍增一次//求第二关键字的lv值//由于从n - cl + 1的位置开始,与其合并的元素都在外面,因此这些位置的第二关键字都为0//因此从左往右将其从0开始排名,即lv[0 ~ 1-cl] = n-cl+1 ~ n  for ( j = 0, i = n - cl + 1; i <= n; i++ ) lv[j++] = i;//接下来对前面的元素进行排序for ( i = 0; i <= n; i++ ) if ( sa[i] >= cl ) lv[j++] = sa[i] - cl;//以上已经完成对第二关键字的排名//接下来又是基数排序的模板算法//利用同时利用第一和第二关键字完成对两个关键字的合并排名for ( i = 0; i <= mv; i++ ) sv[i] = 0;for ( i = 0; i <= n; i++ ) sv[ vi[ lv[i] ] ]++;for ( i = 1; i <= mv; i++ ) sv[i] += sv[i - 1];for ( i = n; i >= 0; i-- ) sa[ --sv[ vi[ lv[i] ] ] ] = lv[i];//但是得到的仅仅是sa数组,还要合并得出vi数组才行//合并得到vi数组,由于要跟新vi的过程中还要利用vi,但是lv数组已经没用了//因此可以交换两者的空间,达到重复利用的效果for ( t = vi, vi = lv, lv = t, vi[ sa[0] ] = 0, v = 1, i = 1; i <= n; i++ )vi[ sa[i] ] = keq( lv, sa[i - 1], sa[i], cl ) ? v - 1 : v++;}rk = vi;//最后vi和rank就完全相等了}voidbd_h( char *s, int n ) {//build height//构造height数组inti, j;intcl;//common length,公共连续子序列的长度//由数学推导的height[ rank[i] ] ≥ height[ rank[i] - 1] - 1//虽然这里乍一看两个for循环应该是n^2的复杂度,其实由于上面的条件控制//是的复杂度降到几乎n//!!注意:s[n] = '\0',因此rank[n] = 0,所以rank[n] - 1 < 0,所以sa[ rk[n] - 1 ]//可能会导致段错误,因此i < n(第一个for语句中)for ( cl = 0, i = 0; i < n; h[ rk[i++] ] = cl )//这里cl为上一轮的height值,--就是上一轮的height值-1(见不等式)//回顾:height[i] = suffix(sa[i - 1])和suffix(sa[i])的最长公共前缀的长度for ( cl ? --cl : 0, j = sa[ rk[i] - 1 ]; s[i + cl] == s[j + cl]; cl++ );}intmain() {intlen;//第一串字符串的长度intn;//合并后的总长度(包括'\0')intans;inti;//计数变量//合并字符串scanf("%s", s);len = strlen(s);s[len] = '#';scanf("%s", s + len + 1);n = len + strlen( s + len );bd_sa( s, n, CHN );bd_h( s, n );//最长公共连续子序列长度必然为height中的最大值//但是注意要剪枝,并不是最大的height就一定符合要求for ( ans = 0, i = 1; i <= n; i++ )if ( h[i] > ans )//只有到两个排名相邻的后缀长度在第一个字符串长度两边时才表名//这两个后缀中分别包含了前后两个字符串中内容//否则这两个后缀都只包含一个字符串中的内容,因此无公共子串而言//注意这里的剪枝if ( MIN( sa[i - 1], sa[i] ) < len && MAX( sa[i - 1], sa[i] ) > len )ans = h[i];printf("%d\n", ans);return 0;}

无注释代码:

#include <string.h>#include <stdio.h>#defineCHN'z'#defineMAXLEN200002#defineMAX(x,y)( (x) > (y) ? (x) : (y) )#defineMIN(x,y)( (x) < (y) ? (x) : (y) )intbuf1[MAXLEN];intbuf2[MAXLEN];intbuf3[MAXLEN];intsa[MAXLEN];inth[MAXLEN];int*rk;chars[MAXLEN];intkeq( int *v, int x, int y, int l ) {return v[x] == v[y] && v[x + l] == v[y + l];}voidbd_sa( char *s, int n, int mv ) {int *vi;int*sv;int *lv;int*t;intv;intcl;inti, j;vi = buf1;sv = buf2;lv = buf3;for ( i = 0; i <= mv; i++ ) sv[i] = 0;for ( i = 0; i <= n; i++ ) sv[ vi[i] = s[i] ]++;for ( i = 1; i <= mv; i++ ) sv[i] += sv[i - 1];for ( i = n; i >= 0; i-- ) sa[ --sv[ vi[i] ] ] = i;for ( cl = 1, v = 0; v <= n; cl <<= 1, mv = v ) {for ( j = 0, i = n - cl + 1; i <= n; i++ ) lv[j++] = i;for ( i = 0; i <= n; i++ ) if ( sa[i] >= cl ) lv[j++] = sa[i] - cl;for ( i = 0; i <= mv; i++ ) sv[i] = 0;for ( i = 0; i <= n; i++ ) sv[ vi[ lv[i] ] ]++;for ( i = 1; i <= mv; i++ ) sv[i] += sv[i - 1];for ( i = n; i >= 0; i-- ) sa[ --sv[ vi[ lv[i] ] ] ] = lv[i];for ( t = vi, vi = lv, lv = t, vi[ sa[0] ] = 0, v = 1, i = 1; i <= n; i++ )vi[ sa[i] ] = keq( lv, sa[i - 1], sa[i], cl ) ? v - 1 : v++;}rk = vi;}voidbd_h( char *s, int n ) {inti, j;intcl;for ( cl = 0, i = 0; i < n; h[ rk[i++] ] = cl )for ( cl ? --cl : 0, j = sa[ rk[i] - 1 ]; s[i + cl] == s[j + cl]; cl++ );}intmain() {intlen;intn;intans;inti;scanf("%s", s);len = strlen(s);s[len] = '#';scanf("%s", s + len + 1);n = len + strlen( s + len );bd_sa( s, n, CHN );bd_h( s, n );for ( ans = 0, i = 1; i <= n; i++ )if ( h[i] > ans )if ( MIN( sa[i - 1], sa[i] ) < len && MAX( sa[i - 1], sa[i] ) > len )ans = h[i];printf("%d\n", ans);return 0;}

单词解释:

frequently:adv, 频繁地,时常

mobile:adj, 机动的,不固定的

append:vt, 附加,追加

redundancy:n, 冗余

issue:n, 问题,故障

charge:vt, 收费

persuade:vt, 说服

dedicate:vt, 致力于,献给

beloved:adj, 心爱的,挚爱的

0 0