Long Long Message(后缀数组求最长公共子串)

本文介绍了一种算法,用于找出两个长字符串中的最长公共连续子串,以帮助小猫计算发送给母亲短信的最可能原始文本长度,从而优化短信费用。

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

题目链接:https://vjudge.net/problem/POJ-2774

The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes to him these days: his mother is getting ill. Being worried about spending so much on railway tickets (Byterland is such a big country, and he has to spend 16 shours on train to his hometown), he decided only to send SMS with his mother.

The little cat lives in an unrich family, so he frequently comes to the mobile service center, to check how much money he has spent on SMS. Yesterday, the computer of service center was broken, and printed two very long messages. The brilliant little cat soon found out:

1. All characters in messages are lowercase Latin letters, without punctuations and spaces.
2. All SMS has been appended to each other – (i+1)-th SMS comes directly after the i-th one – that is why those two messages are quite long.
3. His own SMS has been appended together, but possibly a great many redundancy characters appear leftwards and rightwards due to the broken computer.
E.g: if his SMS is “motheriloveyou”, either long message printed by that machine, would possibly be one of “hahamotheriloveyou”, “motheriloveyoureally”, “motheriloveyouornot”, “bbbmotheriloveyouaaa”, etc.
4. For these broken issues, the little cat has printed his original text twice (so there appears two very long messages). Even though the original text remains the same in two printed messages, the redundancy characters on both sides would be possibly different.

You are given those two very long messages, and you have to output the length of the longest possible original text written by the little cat.

Background:
The SMS in Byterland mobile service are charging in dollars-per-byte. That is why the little cat is worrying about how long could the longest original text be.

Why ask you to write a program? There are four resions:
1. The little cat is so busy these days with physics lessons;
2. The little cat wants to keep what he said to his mother seceret;
3. POJ is such a great Online Judge;
4. The little cat wants to earn some money from POJ, and try to persuade his mother to see the doctor :(

Input

Two strings with lowercase letters on two of the input lines individually. Number of characters in each one will never exceed 100000.

Output

A single line with a single integer number – what is the maximum length of the original text written by the little cat.

Sample Input

yeshowmuchiloveyoumydearmotherreallyicannotbelieveit
yeaphowmuchiloveyoumydearmother

Sample Output

27

这道题要求的是两个串的最长公共连续子串,在求出height数组之后,再把sa数组区分出来,只要其中一个sa数组是属于第一串,另外一个属于第二串,那么我们可以求得其最大值,之所以可以这样做,是因为sa数组已经对字符串按字典序排好序了 

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
const int MAXN=2e5+10;

int sa[MAXN];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
             //的后缀的开头位置顺次放入SA中
int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
int rank[MAXN],height[MAXN];
//待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
//除s[n-1]外的所有s[i]都大于0,r[n-1]=0
//函数结束以后结果放在sa数组中
void build_sa(int s[],int n,int m)
{
    int i,j,p,*x=t1,*y=t2;
    //第一轮基数排序,如果s的最大值很大,可改为快速排序
    for(i=0;i<m;i++)c[i]=0;
    for(i=0;i<n;i++)c[x[i]=s[i]]++;
    for(i=1;i<m;i++)c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        //直接利用sa数组排序第二关键字
        for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
        //这样数组y保存的就是按照第二关键字排序的结果
        //基数排序第一关键字
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[y[i]]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        //根据sa和x数组计算新的x数组
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n)break;
        m=p;//下次基数排序的最大值
    }
}
void getHeight(int s[],int n)
{
    int i,j,k=0;
    for(i=0;i<=n;i++)rank[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)k--;
        j=sa[rank[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rank[i]]=k;
    }
}
 
char str[MAXN];
int s[MAXN];
 
int main()
{
    scanf("%s",str);
    int len=strlen(str);
    str[len]='$';
    scanf("%s",str+len+1);
    int n=strlen(str);
    for(int i=0;i<=n;i++) s[i]=str[i];
    build_sa(s,n+1,128);
    getHeight(s,n);
    int ans=0;
    for(int i=2;i<=n-1;i++){
    	if(height[i]>ans){
    		if(sa[i-1]>=0&&sa[i-1]<len&&sa[i]>=len) ans=max(ans,height[i]);
    		if(sa[i]>=0&&sa[i]<len&&sa[i-1]>=len) ans=max(ans,height[i]);
		}
	}
	printf("%d\n",ans);
    return 0;
}

 

<think>我们正在处理SpringKafka中消息监听器抛出的MessageConversionException异常。根据引用[3],我们知道MessageConversionException被DefaultErrorHandler视为fatal异常,因此不会重试,而是立即调用恢复器(如果配置了的话)。处理MessageConversionException的步骤:1.理解异常原因:MessageConversionException通常发生在消息转换失败时,比如消息格式与预期不符(例如,期望是JSON但实际是字符串,或者反序列化失败)。2.配置错误处理器(ErrorHandler):我们可以使用DefaultErrorHandler,并可以自定义其行为。由于MessageConversionException是fatal的,默认不会重试,所以我们可能需要配置一个恢复器(Recoverer)来处理这些无法转换的消息。3.使用恢复器:恢复器可以记录失败的消息,或者将其发送到一个死信队列(DeadLetter Queue, DLQ)等。4.调整错误处理:如果需要,我们也可以调整哪些异常被视为fatal(例如,如果我们希望重试某些转换异常,虽然通常不建议,因为转换问题在重试时不太可能解决)。具体配置示例:我们可以创建一个DefaultErrorHandler,并设置一个恢复器(比如DeadLetterPublishingRecoverer)来处理不可重试的异常(包括MessageConversionException)。同时,我们可以自定义fatal异常列表。步骤: a.创建并配置一个DeadLetterPublishingRecoverer,用于将转换失败的消息发布到死信主题。 b.创建DefaultErrorHandler,设置恢复器,并可以调整不可重试的异常列表(如果需要的话)。 c.将错误处理器设置到消息监听容器中。示例代码:首先,确保你的配置类中注入了必要的bean,比如KafkaTemplate(用于发送死信消息)。配置错误处理器:```java@Configuration@EnableKafkapublic classKafkaConfig{@Beanpublic ConcurrentKafkaListenerContainerFactory<String, Object> kafkaListenerContainerFactory(ConsumerFactory<String,Object>consumerFactory,KafkaTemplate<String,Object>kafkaTemplate){ConcurrentKafkaListenerContainerFactory<String,Object>factory =new ConcurrentKafkaListenerContainerFactory<>();factory.setConsumerFactory(consumerFactory);//创建DeadLetterPublishingRecoverer,指定死信消息发送的KafkaTemplateDeadLetterPublishingRecovererrecoverer= newDeadLetterPublishingRecoverer(kafkaTemplate,(record, exception) ->{//决定死信主题的名称,这里我们使用原主题加".DLT"returnnew TopicPartition(record.topic() +".DLT",record.partition());});//创建DefaultErrorHandler,并设置恢复器DefaultErrorHandlererrorHandler= newDefaultErrorHandler(recoverer);//可以自定义不可重试的异常列表(可选)//默认已经包含MessageConversionException,如果你希望移除它(即允许重试),可以这样做(但通常不推荐)://errorHandler.removeNotRetryableException(MessageConversionException.class);//设置错误处理器factory.setErrorHandler(errorHandler);returnfactory;}} ```注意:上面的配置中,我们为每个监听器容器工厂设置了错误处理器。DeadLetterPublishingRecoverer会将失败的消息发送到原主题加上".DLT"后缀的主题中。另外,根据引用[3]中的信息,我们还可以配置`commitRecovered`属性(如果使用手动确认模式),但在这个例子中我们使用的是DefaultErrorHandler,它默认会提交恢复的记录(如果恢复器成功处理)。在SpringKafka2.8及以上版本,DeadLetterPublishingRecoverer会标记记录为已处理(从而提交偏移量),除非恢复失败。如果消息转换失败是由于消息格式问题,那么重试通常没有意义,因此直接进入恢复流程(发送到死信队列)是合理的。另外,我们也可以考虑在消息转换之前进行日志记录或者添加其他自定义处理。例如,我们可以实现自定义的MessageConverter,在转换失败时记录错误,但使用错误处理器是更通用的做法。问题:如果消息转换失败,那么监听器方法根本就不会被调用,因此错误处理器会捕获到由消息转换器抛出的异常。总结:通过配置DefaultErrorHandler并搭配DeadLetterPublishingRecoverer,我们可以处理MessageConversionException,将无法转换的消息发送到死信队列,以便后续分析。相关问题:1.如何在Spring Kafka中配置死信队列(DLQ)?2.除了使用死信队列,还有哪些处理不可消费消息的方法?3.如何自定义Spring Kafka的消息转换器(MessageConverter)来处理特定格式的消息?4.在SpringKafka中,如何手动确认消息?它与错误处理有什么关系?</think>### Spring Kafka 处理 MessageConversionException 的方法 当 Spring Kafka 监听器遇到 `MessageConversionException` 时(通常由消息格式不匹配或反序列化失败引起),可参考以下处理方法: #### 1. **配置错误处理器 (ErrorHandler)** 使用 `DefaultErrorHandler` 处理不可恢复的异常: ```java @Bean public DefaultErrorHandler errorHandler() { DefaultErrorHandler handler = new DefaultErrorHandler(); // 添加自定义恢复逻辑(如发送到死信队列) handler.setRecoverer((record, ex) -> { // 处理失败消息:记录日志/发送到死信队列 System.err.println("处理失败的消息: " + record.value()); }); return handler; } // 注入监听器容器工厂 @Bean public ConcurrentKafkaListenerContainerFactory<String, String> containerFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setErrorHandler(errorHandler()); return factory; } ``` > **说明**: > - `MessageConversionException` 默认被视为 **fatal 异常**,不会重试[^3] > - 通过 `setRecoverer()` 可自定义恢复逻辑(如记录日志或转发到死信队列) #### 2. **启用死信队列 (DLQ)** 配置自动转发失败消息到死信主题: ```yaml spring: kafka: listener: type: batch ack-mode: RECORD properties: spring.json.trusted.packages: "*" # 允许反序列化任意包 ``` 在代码中配置死信发布: ```java @Bean public DeadLetterPublishingRecoverer dlqRecoverer(KafkaTemplate<?,?> template) { return new DeadLetterPublishingRecoverer(template, (record, ex) -> new TopicPartition(record.topic() + ".DLT", -1) ); } ``` #### 3. **消息转换器调优** 检查并配置合适的消息转换器: ```java @Bean public RecordMessageConverter converter() { // 使用 JSON 转换器并配置可信包 JsonMessageConverter converter = new JsonMessageConverter(); converter.getTypeMapper().addTrustedPackages("*"); return converter; } // 注入到监听器工厂 factory.setMessageConverter(converter()); ``` #### 4. **验证消息格式一致性** 确保生产者和消费者使用相同序列化格式: ```yaml spring: kafka: producer: value-serializer: org.springframework.kafka.support.serializer.JsonSerializer consumer: value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer ``` #### 5. **添加异常拦截器** 在监听方法中捕获异常: ```java @KafkaListener(topics = "test-topic") public void listen(String message) { try { // 业务处理逻辑 } catch (MessageConversionException ex) { // 自定义处理:记录指标/告警等 metricsService.recordError(ex); } } ``` ### 关键注意事项 1. **Fatal 异常特性**: `MessageConversionException` 默认被归类为不可重试异常,首次失败即触发恢复逻辑[^3]。 2. **事务环境差异**: 若启用 Kafka 事务,需改用 `DefaultAfterRollbackProcessor` 处理异常[^3]。 3. **死信队列提交**: 当使用 `AckMode.MANUAL_IMMEDIATE` 时,设置 `commitRecovered=true` 确保已恢复记录的偏移量被提交[^3]。 4. **自定义异常分类**: 可通过 API 调整异常分类策略: ```java handler.addNotRetryableException(CustomException.class); // 添加新不可重试异常 ``` ### 相关问题 1. 如何在 Spring Kafka 中配置死信队列并实现自动重试? 2. 反序列化异常(`DeserializationException`)与消息转换异常有何区别?处理方式是否相同? 3. 如何监控 Spring Kafka 监听器的消息处理失败率? 4. 在分布式事务场景下,Kafka 消息异常处理有哪些特殊要? [^1]: Kafka 监听器基础配置参考 [^2]: 监听器执行异常通用处理原则 [^3]: DefaultErrorHandler 对 fatal 异常的处理机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值