manacher-線性查找演算法-(最長迴文子串問題)

来源:https://www.cnblogs.com/hujunxiang98/archive/2020/05/21/12934173.html
-Advertisement-
Play Games

manacher 線性查找演算法 manacher演算法中需要知道的概念: 迴文半徑: 迴文中心 到 迴文邊界的距離. 迴文半徑數組: radius[i]表示以 i 為迴文中心的最大迴文半徑. 迴文最右邊界: 出現的迴文邊界中最右的位置. 首次迴文中心: 迴文最右邊界首次出現時的迴文中心. 首次迴文左邊 ...


manacher-線性查找演算法
manacher演算法中需要知道的概念:
   迴文半徑: 迴文中心 到 迴文邊界的距離.
   迴文半徑數組: radius[i]表示以 i 為迴文中心的最大迴文半徑.
   迴文最右邊界: 出現的迴文邊界中最右的位置.
   首次迴文中心: 迴文最右邊界首次出現時的迴文中心.
   首次迴文左邊界: 迴文最右邊界首次出現時的迴文左邊界.
   i鏡像點i': 若i位置在長迴文串中, 那麼i位置 以 長迴文串的迴文中心 作出的 對稱點.

manacher 流程:
   預處理字元串: 例如將s1="sasd"處理成s2="#s#a#s#d#".
   將迴文最右邊界和首次迴文中心都置為最左邊界即-1索引.
   遍歷處理後的字元串s2, 根據 i位置 與 迴文最右邊界 的關係可分為2種情況:
	  i位置在迴文最右邊界外面:
		 radius[i] = 暴力中心擴展後的迴文半徑.
	  i位置在迴文最右邊界裡面(包括迴文最右邊界), 則根據 i鏡像點i'的迴文左邊界 與 首次迴文左邊界 的關係又可分為3種情況:
		 i'的迴文左邊界在首次迴文左邊界裡面(不包括首次迴文左邊界):
			radius[i] = i鏡像點i'的最長迴文半徑(即是radius[i']).
		 i'的迴文左邊界在首次迴文左邊界外面:
			radius[i] = 迴文最右邊界與i位置的距離.
		 i'的迴文左邊界在首次迴文左邊界的邊界上:
			radius[i] = 暴力中心擴展後的迴文半徑.
	  經過上面得到radius[i]後,根據radius[i]的狀況更新迴文最右邊界,首次迴文中心,首次迴文左邊界,全局最大迴文半徑等等信息.




創建3個文件:manacher.h、manacherArray.c、manacherArrayTest.c。




manacherArray.h
#ifndef MANACHER_ARRAY_H_
#define MANACHER_ARRAY_H_

// 功能: 最長迴文子串的長度.
// 參數: s(字元串首地址).
// 返回: 最長迴文子串的長度.
// 註意: 當 s=NULL 時, 將錯誤退出程式.
extern int32_t manacherArray( char s[] );

#endif

manacherArray.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "manacherArray.h"

// 功能: 列印錯誤信息後就錯誤退出程式.
// 參數: expression(錯誤判斷表達式), message(需列印的錯誤信息).
// 返回: 無.
// 註意: 當表達式 expression 為真時, 才觸發.
#define ERROR_EXIT( expression, message )                                    \
if( (expression) ) {                                                         \
	fprintf( stderr, "\nerror location: file = %s, func = %s, line = %d.\n", \
	                 __FILE__, __func__, __LINE__ );                         \
	fprintf( stderr, "error  message: %s%s.\n\a",                            \
	                 (message) != NULL ? (message) : __func__,               \
	                 (message) != NULL ? "" : " function error" );           \
	exit( EXIT_FAILURE );                                                    \
}
  • 萌新版本。
    int32_t manacherArray( char s[] ) {
    	char *tempA = NULL, ch = '#';
    	int *radiusA = NULL; // 迴文半徑數組.
    	int right = -1;      // 迴文最右邊界.
    	int center = -1;     // 首次迴文中心.
    	int left = -1;       // 以 center 為迴文中心的迴文左邊界.
    	int mirror = -1;     // i 以 center 為中心的鏡像 i'.
    	int mirrorLeft = -1; // 以 i' 為迴文中心的迴文左邊界.
    	int i = 0, j = 1, answer = 0, slen = 0;
    
    	ERROR_EXIT( s == NULL, "NullPointerException" );
    	slen = strlen( s );
    	tempA = malloc( sizeof(*tempA) * (slen * 2 + 2) );
    	for( tempA[0] = ch; (tempA[j++] = s[i++]) != '\0'; tempA[j++] = ch ) {}
    	radiusA = malloc( sizeof(*radiusA) * (slen * 2 + 1) );
    
    	for( i = 0; tempA[i] != '\0'; ++i ) {
    		if( i > right ) {
    			// i位置在迴文最右邊界外, 暴力擴.
    			for( j = 1; i - j >= 0 && tempA[i - j] == tempA[i + j]; ++j ) {}
    			radiusA[i] = j - 1;
    		} else {
    			left = center - radiusA[center];       // 以 center 為迴文中心的迴文左邊界.
    			mirror = center * 2 - i;               // i 以 center 為中心的鏡像 i'.
    			mirrorLeft = mirror - radiusA[mirror]; // 以 i' 為迴文中心的迴文左邊界.
    			// i位置在迴文最右邊界裡面,根據以 i' 為迴文中心的迴文左邊界又可分為3種情況.
    			if( mirrorLeft == left ) {
    				for( j = right - i + 1; i - j >= 0 && tempA[i - j] == tempA[i + j]; ++j ) {}
    				radiusA[i] = j - 1;
    			} else if( mirrorLeft < left ) {
    				radiusA[i] = right - i;
    			} else {
    				radiusA[i] = radiusA[mirror];
    			}
    		}
    		if( i + radiusA[i] > right ) { // 出現較右的迴文右邊界.
    			center = i;
    			right = i + radiusA[i];
    		}
    		if( radiusA[i] > answer ) {    // 出現較大的迴文半徑.
    			answer = radiusA[i];
    		}
    	}
    	free( radiusA );
    	free( tempA );
    
    	return answer;
    }
    
  • 左神版本。
    char *manacherArray( char s[] ) {
    	char *ta = NULL, ch = '#';
    	int *radius = NULL;  // 迴文半徑數組.
    	int right = -1;      // 出現過的迴文最右邊界.
    	int center = -1;     // 第一次出現迴文最右邊界時的迴文中心.
    	int answer = -1;
    	int i = 0, j = 1, slen = 0;
    
    	ERROR_EXIT( s == NULL, "NullPointerException" );
    	slen = strlen( s );
    	ta = malloc( sizeof(*ta) * (slen * 2 + 2) );
    	for( ta[0] = ch; (ta[j++] = s[i++]) != '\0'; ta[j++] = ch ) {}
    	radius = malloc( sizeof(*radius) * (slen * 2 + 1) );
    
    	for( i = 0; ta[i] != '\0'; ++i ) {
    		// i在迴文最右邊界外,讓 radius[i] 預設取只有自己本身距離1.
    		// i在迴文最右邊界內,讓 radius[i] 預設取較短的距離, 因為較長距離會越界或不是迴文.
    		// center * 2 - i 表示 i位置的鏡像i'.
    		radius[i] = i >= right ? 1 : MIN( radius[center * 2 - i], right - i );
    
    		// 為什麼 while 迴圈 不用判斷 i + radius[i] < strlen( ta ) 呢?
    		// ∵ 迴圈中判斷了 i - radius[i] >= 0.
    		// ∴ 在迴圈中 i + radius[i] 是合法索引不會引發越界.
    		// ∵ i 和 radius[i] 不會是負數 且 radius[i]在迴圈開始前的預設最小值是1.
    		// ∴ i - radius[i] 位置 永遠在 i + radius[i] 位置 前面.
    		// ∵ radius[i] 在迴圈中的增長值是1.
    		// ∴ '\0'出現的位置一定是在 i + radius[i] 位置.
    		while( i - radius[i] >= 0 && ta[i - radius[i]] == ta[i + radius[j]] ) {
    			++radius[i];
    		}
    
    		--radius[i]; // 減去i位置自己本身距離1.
    
    		if( i + radius[i] > right ) { // 出現較右的迴文右邊界.
    			center = i;
    			right = i + radius[i];
    		}
    		if( radius[i] > answer ) {    // 出現較大的迴文半徑.
    			answer = radius[i];
    		}
    	}
    	free( radius );
    	free( ta );
    
    	return answer;
    }
    

manacherArrayTest.c
實現對數器

manacherArrayTest.sh
# !/bin/bash

for(( i = 1; i <= 21; ++i )) do
	printf "%02d" ${i}
	echo -n ______________
	./manacherArrayTest
done

參考: https://www.zhihu.com/question/37289584
參考: https://segmentfault.com/a/1190000008484167

參考書籍:
[1] 嚴蔚敏,吳偉民.數據結構(C語言版)[M].北京:清華大學出版社,2007.
[2] 高一凡.《數據結構》演算法實現及解析[M].第二版.西安:西安電子科技大學出版社,2004.
[3] Andrew Binstock,John Rex著,陳宗斌等譯.Practical Algorithms for Programmers.北京:機械工業出版社,2009.

重點參考人物: 左神.




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 從水仙花數引出自冪數,並做簡單的介紹,並不做深入的瞭解,同時使用C++代碼初略的實現9位數之內的自冪數輸出! ...
  • 1 簡介 眾所周知(你不知也當你知), 是以文檔( )組織數據的。除了常用於存儲 數據,它也是可以存儲普通文件的。我們可以把一些文件以 的格式存入 ,十分方便,比較說圖片、文本文件等。但 的`BSON Document 16MB MongoDB GridFS 16MB GridFS`存儲。 2 基本 ...
  • 【註】c++這塊的日誌類,很容易因為編譯環境設置字元集,參數類型等問題帶來一些不方便調用的問題。 那麼一開始可以在日誌中提供寬位元組類型的傳參處理。否則可能會部分情況下可以,部分情況下會丟失信息。 Log.h文件內容 #pragma once #include "stdio.h" #include " ...
  • 問題:控制台tomcat日誌亂碼,網站頁面亂碼 配置:idea2019.2、win10、tomcat8.5 字元集: idea2019.2 File Encoding UTF-8 win10 系統預設GBK tomcat8.5 UTF-8 控制台亂碼 1、將tomcat目錄下conf\logging ...
  • 一、電腦要完成兩數相加,可以大致分為如下幾個步驟: 1.從記憶體位置2000上把一個數字拷貝到寄存器1; 2.從記憶體位置2004上把另一個數字拷貝到寄存器2; 3.把寄存器2裡面的內容與寄存器1中的內容相加,把結果儲存在寄存器1中。 4.把寄存器1中的內容拷貝到記憶體位置2008。 二、高級語言以更抽 ...
  • 今天是521,就分享一個程式員必會的——情侶回憶殺《愛情電子相冊》吧!話不多說,先上思路,後接源碼! 具備能力: 1.基本可視化編程 1.1 initgraph(800,600); 1.2 關閉視窗closegraph(); 1.3 視窗坐標 2.基本繪圖函數 2.1 line 畫線 2.2 cir ...
  • Level 7kyu :Two to One 取2個字元串s1,s2僅包含從a到的字母z。 返回一個新的排序字元串,最長的字元串,包含不同的字母, 每個僅取一次-來自s1或s2。 主要方法: toCharArray()->字元串轉字元數組 arraycopy(from,0,to,0,長度)->複製數 ...
  • Level 7kyu :Get the Middle Character 您將得到一個單詞。 您的工作是返回單詞的中間字元。 要求: 如果單詞的長度是奇數,則返回中間字元。 如果單詞的長度是偶數,請返回中間的2個字元。 主要方法: length()->獲取字元串長度 charAt(索引下標)->返回 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...