最长重复子序列问题的后缀数组解法

本文介绍使用后缀数组求解最长重复子序列的方法,并以banana为例详细阐述了实现过程。此外还讨论了如何寻找至少出现过n次的最长重复子序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最长重复子序列问题除了使用穷举法,还可以使用后缀数组和后缀树来求解。这里给出使用后缀数组解决的最长重复子序列的过程,并以“banana”为例进行演示。首先写下一个比较两个字符串从头开始共同部分的长度的函数:

int comlen(char *p, char *q) {
    int i = 0;
    while(*p&&(*p++ == *q++))
        i++;
    return i;
}

设定该程序最多处理MAXN个字符,并存放在数组c中,a是对应的后缀数组:

#define MAXN 5000000
char c[MAXN],*a[MAXN];

读取输入时,对a进行初始化:

//n是已读入的字符数目
int input(int n) {
    int ch;
    while((ch = getchar())!=EOF) {
        a[n] = &c[n];
        c[n++] = ch;
    }
    c[n] = 0;
    return n;
}

这样,对于“banana”,对应的后缀数组为:
  a[0]:banana
  a[1]:anana
  a[2]:nana
  a[3]:ana
  a[4]:na
  a[5]:a
  它们是"banana"的所有后缀,这也是“后缀数组”命名原因。
  如果某个长字符串在数组c中出现了两次,那么它必然出现在两个不同的后缀中,更准确的说,是两个不同后缀的同一前缀。通过排序可以寻找相同的前缀,排序后的后缀数组为:
a[0]:a
a[1]:ana
a[2]:anana
a[3]:banana
a[4]:na
a[5]:nana
  扫描排序后的数组的相邻元素就能得到最长的重复字串,本例为“ana”。
  这里做一个扩展:(习题16.8)如何寻找至少出现过n次的最长重复子序列?
  解法是比较a[i...i+n]中第一个和最后一个的最长公共前缀长度,上文是对n=1的特例。因此写出一般化的扫描函数:

int scan(int n,int k) {
    int maxi=0,i;
    int temp,maxlen = 0;
    for(i=0;i<n-k;i++) {
        temp = comlen(a[i],a[i+k]);
        if(temp>maxlen) {
            maxlen = temp;
            maxi = i;
        }
    }
    printf("%d times the longest:%.*s\n",k,maxlen,a[maxi]);
    return 0;
}
int pstrcmp(char **p, char **q)
{   return strcmp(*p, *q); }


int main(void) {
    int i,n;
    n = input(0);
    qsort(a,n,sizeof(char *),pstrcmp);
    printf("\n");
    for(i=0;i<n;i++)
        printf("%s\n",a[i]);
    scan(n,1);
    scan(n,2);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值