Period(NEXT)

字符串周期性检测

Period

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11087 Accepted Submission(s): 5207

Problem Description

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as AK , that is A concatenated K times, for some string A. Of course, we also want to know the period K.

Input

The input file consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S. The second line contains the string S. The input file ends with a line, having the number zero on it.

Output

For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.

Sample Input

3
aaa
12
aabaabaabaab
0

Sample Output

Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4

思路:
这个题就是使用next[]十足,next[i]是从0~i的字符串的前缀和后缀相同的长度。
i+1-next[i]就是周期长度,如果(i+1)%temp==0 && (i+1)/temp>1,满足,输出。

#include <iostream>
using namespace std;
#define MAXN 1000005
int Next[MAXN];
string s;

void makeNext(string s1,int Next[]){
    int q,k;
    int m=s1.length();
    Next[0]=0;
    for(int q=1,k=0;q<m;q++){   
        while(k>0 && s1[q]!=s1[k])
            k=Next[k-1];
        if(s1[q]==s1[k]){
            k++;
        }
        Next[q]=k;
    }
}

int main(){
    std::ios::sync_with_stdio(false);  
    int n,t=0;
    while(cin>>n && n!=0){
        t++;
        cin>>s;
        makeNext(s,Next);
        cout<<"Test case #"<<t<<endl;
        for(int i=1;i<n;i++){
            int temp=i+1-Next[i];
            if((i+1)%temp==0 && (i+1)/temp>1)
                cout<<i+1<<" "<<(i+1)/temp<<endl;
        }
        cout<<endl;
    }
    return 0;

} 
package jnpf.task; import jnpf.entity.EquipmentparameterEntity; import jnpf.entity.UleabnormalrecordsEntity; import jnpf.myutils.DateUtils; import jnpf.service.EquipmentparameterService; import jnpf.service.RealDataService; import jnpf.service.UleabnormalrecordsService; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.time.; import java.time.format.DateTimeFormatter; import java.util.; import java.util.stream.Collectors; @Slf4j @Component public class HAHFAnalysisTask { private static final Logger logger = LoggerFactory.getLogger(HAHFAnalysisTask.class); @Autowired private EquipmentparameterService equipmentparameterService; @Autowired private UleabnormalrecordsService uleabnormalrecordsService; @Autowired private RealDataService realDataService; //@XxlJob("hahfAnalysis") public void hahfAnalysis() { logger.info("HAHF分析任务启动"); // 1. 获取TSP设备列表 List<EquipmentparameterEntity> equipmentList = equipmentparameterService.geHAHFList(); if (equipmentList.isEmpty()) { logger.info("没有可分析的TSP设备"); return; } // 2. 准备查询参数 Map<String, Object> params = new HashMap<>(); List<String> tags = equipmentList.stream() .map(EquipmentparameterEntity::getTag) .collect(Collectors.toList()); params.put("tags", tags); // 3. 获取日常值数据 params.put("begindate", DateUtils.dataTimeformat(new Date())); List<Map> dailyValueMaps = realDataService.selectLastmonthDailyvalue(params); Map<String, Map> dailyValueMap = dailyValueMaps.stream() .collect(Collectors.toMap( m -> (String) m.get("datag"), m -> m )); // 4. 获取小时数据(前一天到当前) params.put("begindate", timeCalculation()); params.put("enddate", DateUtils.dataTimeformat(new Date())); List<Map> hourDataMaps = realDataService.selectHourdataByTagIdsAndDate(params); // 5. 按tag分组小时数据 Map<String, List<Map>> hourDataByTag = hourDataMaps.stream() .collect(Collectors.groupingBy( m -> (String) m.get("datag") )); // 6. 获取当前报警中的记录 List<UleabnormalrecordsEntity> activeAlarms = uleabnormalrecordsService.getOnhahfList(); Map<String, UleabnormalrecordsEntity> activeAlarmMap = activeAlarms.stream() .collect(Collectors.toMap( UleabnormalrecordsEntity::getParacode, alarm -> alarm )); // 7. 处理每个设备 for (EquipmentparameterEntity equipment : equipmentList) { try { processEquipment( equipment, dailyValueMap, hourDataByTag, activeAlarmMap ); } catch (Exception e) { logger.error("处理设备{}失败: {}", equipment.getEncode(), e.getMessage()); } } logger.info("HAHF分析任务完成"); } private void processEquipment(EquipmentparameterEntity equipment, Map<String, Map> dailyValueMap, Map<String, List<Map>> hourDataByTag, Map<String, UleabnormalrecordsEntity> activeAlarmMap) { String tag = equipment.getTag(); String paraCode = equipment.getEncode(); // 获取该tag的日常值配置 Map dailyConfig = dailyValueMap.get(tag); if (dailyConfig == null) { logger.warn("设备{}缺少日常值配置", paraCode); return; } double farBeyondValue = ((Number) dailyConfig.get("farbeyondvalue")).doubleValue(); double limitingValue = ((Number) dailyConfig.get("limitingvalue")).doubleValue(); // 获取该tag的小时数据 List<Map> hourDataList = hourDataByTag.getOrDefault(tag, new ArrayList<>()); // 处理报警状态 if (activeAlarmMap.containsKey(paraCode)) { handleActiveAlarm( activeAlarmMap.get(paraCode), hourDataList, farBeyondValue, limitingValue ); } else { handleNewAlarm( equipment, hourDataList, farBeyondValue, limitingValue ); } } /** * 处理新报警检测 */ private void handleNewAlarm(EquipmentparameterEntity equipment, List<Map> hourDataList, double farBeyondValue, double limitingValue) { // 1. 准备时间序列数据 List<DataPoint> dataPoints = hourDataList.stream() .map(map -> new DataPoint( (String) map.get("datadt"), ((Number) map.get("avgdata")).doubleValue() )) .sorted(Comparator.comparing(DataPoint::getDateTime)) .collect(Collectors.toList()); // 2. 寻找所有符合条件的连续时间段 List<TimePeriod> validPeriods = findValidPeriods(dataPoints, farBeyondValue, limitingValue); // 3. 合并重叠时间段 List<TimePeriod> mergedPeriods = mergePeriods(validPeriods); // 4. 创建报警记录 for (TimePeriod period : mergedPeriods) { UleabnormalrecordsEntity record = new UleabnormalrecordsEntity(); record.setParacode(equipment.getEncode()); record.setAbnormaltime(toDate(period.start)); // 设置开始时间 record.setCompletetime(toDate(period.end)); // 设置结束时间 // 设置其他必要字段 record.setAbnormaltype("HAHF"); // 高频高幅报警 record.setAbnormalstate(period.end == null ? "0" : "1"); // 0-报警中, 1-已结束 uleabnormalrecordsService.save(record); logger.info("创建新报警记录: {} - {} 至 {}", equipment.getEncode(), period.start, period.end); } } /** * 处理活跃报警的恢复检测 */ private void handleActiveAlarm(UleabnormalrecordsEntity activeAlarm, List<Map> hourDataList, double farBeyondValue, double limitingValue) { // 1. 将Date转换为LocalDateTime LocalDateTime alarmStartTime = toLocalDateTime(activeAlarm.getAbnormaltime()); // 2. 准备时间序列数据(从报警开始时间起) List<DataPoint> dataPoints = hourDataList.stream() .map(map -> new DataPoint( (String) map.get("datadt"), ((Number) map.get("avgdata")).doubleValue() )) .filter(point -> !point.dateTime.isBefore(alarmStartTime)) .sorted(Comparator.comparing(DataPoint::getDateTime)) .collect(Collectors.toList()); // 3. 检查是否恢复 LocalDateTime recoveryTime = checkRecovery( dataPoints, alarmStartTime, farBeyondValue, limitingValue ); // 4. 如果恢复则更新记录 if (recoveryTime != null) { activeAlarm.setCompletetime(toDate(recoveryTime)); activeAlarm.setAbnormalstate("1"); // 设置为已结束 uleabnormalrecordsService.updateById(activeAlarm); logger.info("报警{}已恢复,结束时间: {}", activeAlarm.getId(), recoveryTime); } } /** * 核心算法:寻找有效时间段 */ private List<TimePeriod> findValidPeriods(List<DataPoint> dataPoints, double farBeyondValue, double limitingValue) { List<TimePeriod> validPeriods = new ArrayList<>(); if (dataPoints.size() < 24) return validPeriods; // 滑动窗口检测 int startIdx = 0; while (startIdx <= dataPoints.size() - 24) { int endIdx = startIdx + 23; // 24小时窗口 // 检查限制值条件 boolean hasLimitingValue = false; for (int i = startIdx; i <= endIdx; i++) { if (dataPoints.get(i).value >= limitingValue) { hasLimitingValue = true; break; } } if (!hasLimitingValue) { startIdx++; continue; } // 检查6小时分桶条件 boolean[] bucketValid = new boolean[4]; // 4个6小时区间 Arrays.fill(bucketValid, false); for (int i = startIdx; i <= endIdx; i++) { DataPoint point = dataPoints.get(i); int hourBucket = (point.dateTime.getHour() / 6) % 4; if (point.value >= farBeyondValue) { bucketValid[hourBucket] = true; } } // 所有桶都必须满足条件 boolean allBucketsValid = true; for (boolean valid : bucketValid) { if (!valid) { allBucketsValid = false; break; } } if (allBucketsValid) { // 对于当前时间仍在持续的报警,结束时间为null LocalDateTime endTime = (endIdx == dataPoints.size() - 1) ? null : dataPoints.get(endIdx).dateTime; validPeriods.add(new TimePeriod( dataPoints.get(startIdx).dateTime, endTime )); startIdx += 24; // 跳到下一窗口 } else { startIdx++; } } return validPeriods; } /** * 合并重叠时间段 */ private List<TimePeriod> mergePeriods(List<TimePeriod> periods) { if (periods.isEmpty()) return periods; List<TimePeriod> merged = new ArrayList<>(); periods.sort(Comparator.comparing(p -> p.start)); TimePeriod current = periods.get(0); for (int i = 1; i < periods.size(); i++) { TimePeriod next = periods.get(i); // 检查时间段是否重叠或连续 boolean shouldMerge = false; if (current.end != null && next.start != null) { shouldMerge = !next.start.isAfter(current.end.plusHours(1)); } else if (current.end == null) { // 当前时间段仍在持续,合并所有后续时间段 shouldMerge = true; } if (shouldMerge) { // 合并时间段 if (next.end == null) { current.end = null; // 合并后仍在持续 } else if (current.end == null || next.end.isAfter(current.end)) { current.end = next.end; } } else { merged.add(current); current = next; } } merged.add(current); return merged; } /** * 检查报警恢复 */ private LocalDateTime checkRecovery(List<DataPoint> dataPoints, LocalDateTime startTime, double farBeyondValue, double limitingValue) { // 按6小时分桶组织数据 Map<Integer, List<DataPoint>> bucketMap = new HashMap<>(); for (DataPoint point : dataPoints) { int bucketIdx = (point.dateTime.getHour() / 6) % 4; bucketMap.computeIfAbsent(bucketIdx, k -> new ArrayList<>()).add(point); } // 检查每个桶是否仍有有效数据点 LocalDateTime recoveryTime = null; for (Map.Entry<Integer, List<DataPoint>> entry : bucketMap.entrySet()) { boolean hasValidPoint = false; for (DataPoint point : entry.getValue()) { if (point.value >= farBeyondValue) { hasValidPoint = true; break; } } // 如果某个桶没有有效点,取该桶最早时间作为恢复时间 if (!hasValidPoint && !entry.getValue().isEmpty()) { LocalDateTime bucketEnd = entry.getValue().get(entry.getValue().size() - 1).dateTime; if (recoveryTime == null || bucketEnd.isBefore(recoveryTime)) { recoveryTime = bucketEnd; } } } return recoveryTime; } /** * 辅助类:数据点 */ private static class DataPoint { LocalDateTime dateTime; double value; DataPoint(String dateStr, double value) { // 处理可能的时间格式差异 if (dateStr.length() == 13) { // "yyyy-MM-dd HH" this.dateTime = LocalDateTime.parse(dateStr + ":00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } else { this.dateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); } this.value = value; } LocalDateTime getDateTime() { return dateTime; } } /** * 辅助类:时间段 */ private static class TimePeriod { LocalDateTime start; LocalDateTime end; // null表示仍在持续 TimePeriod(LocalDateTime start, LocalDateTime end) { this.start = start; this.end = end; } } /** * 获取前一天开始时间 */ private String timeCalculation() { LocalDate yesterday = LocalDate.now().minusDays(1); LocalDateTime startOfYesterday = yesterday.atStartOfDay(); return startOfYesterday.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } /** * LocalDateTime 转 Date */ private Date toDate(LocalDateTime localDateTime) { if (localDateTime == null) return null; return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); } /** * Date 转 LocalDateTime */ private LocalDateTime toLocalDateTime(Date date) { if (date == null) return null; return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } } 上一问题恢复的代码需要完善几个问题。1,数据有可能缺失,但也要按6小时分析;2、已在报警中的设备,分析出还在报警不操作,报警结束补全记录;3、每个设备应先看数据中最小值是否超过"farbeyondvalue": 远高于日常值和"limitingvalue": 高限值,超过再具体分析。4、报警开始结束时间应为实际发生小时时间。
最新发布
08-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值