基於字符串的編輯距離

最近因爲項目需要對一個web網頁變更進行實時監控,之前項目組有人採用的是比較簡單 的文本對比的方式,只要發現文本中一處發生變動就進行告警,最後導致的結果就是誤報的情況比較多,比如在對首頁進行監控時,後臺對用戶訪問量的統計會在首頁實時更新,這樣就會被當做網站被改動來處理了,而這並不是我們所需要的,因爲我們感興趣的不是網頁動態數據區域的變化,這樣我們最終要解決的就是怎麼來分離網頁的動態區域和靜態區域。

動態區域和靜態區域的分離技術應用在很多方向,數據挖掘方面應用的較廣,如網頁數據抽取、網頁聚類、網頁篡改等等。

在參考了以下論文《 web網頁中動態數據區域的識別去抽取》《基於模板的網頁數據抽取》《基於局部變化性的網頁篡改識別模型及方法》《網頁篡改系統的研究與實現》之後,大體思路就是採用編輯距離來計算樹或字符串的相似度,對字符串的編輯距離計算相當於計算一維向量的編輯距離。

本系統大體也是採用了dom遍歷比較以及節點標記的方式來實現,最終形成一個節點集合,然後對形成的節點集合中相鄰的節點進行合併形成網頁模板。這個也是我大體的方案。

第一個難點是如何去遍歷兩顆dom樹,首先我們先解決比較兩個節點集合中的變化節點,如果採用論文中的for for語句來遍歷的話會出現相鄰節點順序變化卻無法檢測到的情況,並且節點會重複遍歷,而我們想要找出插入、刪除的節點,修改的節點相當於執行了刪除插入操作,而我們這裏將標籤 名沒變化內容或屬性變化的情況稱作修改操作。原先我們採用在for for循環中引入了一個移動標記位來解決也可以一次找出變化的節點,後來發現編輯距離不僅可以解決相似度的問題,而且通過矩陣回溯也可以一次找出變化的節點,一下是標準的字符串編輯距離算法。

值得說明的是,編輯距離計算的前提是隻允許插入、刪除、修改操作來達到字符串轉換的目的。

而我們的算法 也只需要這3中操作,而編輯距離算法本身卻不之侷限這3種操作,具體可參考http://blog.csdn.net/mishifangxiangdefeng/article/details/7925025

而我們也可以去掉某種操作或該重新定義某種操作的行爲,一下是標準的編輯距離計算方法。

// Strings of size m and n are passed.
// Construct the Table for X[0...m, m+1], Y[0...n, n+1]
int EditDistanceDP(char X[], char Y[])
{
	// Cost of alignment
	int cost = 0;
	int leftCell, topCell, cornerCell;
	int minCell = 0;

	int m = strlen(X)+1;
	int n = strlen(Y)+1;

	// T[m][n]
	int *T = (int *)malloc(m * n * sizeof(int));
	//用於矩陣回溯
	int *operation = (int *)malloc(m * n * sizeof(int));

	// Initialize table
	for(int i = 0; i < m; i++)
		for(int j = 0; j < n; j++)
			*(T + i * n + j) = SENTINEL;

	// Set up base cases
	// T[i][0] = i
	for(int i = 0; i < m; i++){
		*(T + i * n) = i;
		*(operation+ i*n)=3;  
	}

	// T[0][j] = j
	for(int j = 0; j < n; j++){
		*(T + j) = j;
		*(operation+j)=2;  
	}
	*operation=0;

	// Build the T in top-down fashion
	for(int i = 1; i < m; i++)
	{
		for(int j = 1; j < n; j++)
		{
			leftCell = *(T + i*n + j-1);
			leftCell += EDIT_COST; 

			topCell = *(T + (i-1)*n + j);
			topCell += EDIT_COST;

			cornerCell = *(T + (i-1)*n + (j-1) );

			// edit[(i-1), (j-1)] = 0 if X[i] == Y[j], 1 otherwise
			cost = (X[i-1] == Y[j-1]?0:1);
			cornerCell += cost; // may be replace

			//如果不回溯則直接調用該函數
			//minCell = Minimum(leftCell, topCell, cornerCell);
	
			if(topCell>leftCell)  
			{  
				if(cornerCell>leftCell)  
				{  
					minCell=leftCell;  
					*(operation + (i)*n + (j))=2;  
				}  
				else  
				{  
					minCell=cornerCell;  
					*(operation + (i)*n + (j))=cost;  
				}  
			}  
			else  
			{  
				if(cornerCell>topCell)  
				{  
					minCell=topCell;  
					*(operation + (i)*n + (j))=3;  
				}  
				else  
				{  
					minCell=cornerCell;  
					*(operation + (i)*n + (j))=cost;  
				}  
			}  
			*(T + (i)*n + (j)) = minCell;
		}
	}

	// 結果存儲在 T[m][n]
	cost = *(T + m*n - 1);

	backtrace(operation, X, Y);
	free(T);
	return cost;
}
通過矩陣回溯可計算出變化的位置

void backtrace(int* operation, char* a, char* b)
{
	int insertion=0,deletion=0,substitution=0;
	int i,j;
	int len1=strlen(a);
	int len2=strlen(b);
	int m = strlen(a)+1;
	int n = strlen(b)+1;

	for (i=len1,j=len2;i>=0&&j>=0;)
	{
		switch(*(operation+i*n+j))
		{
		case 0:
			//printf("(%d,%d) right\n",i,j);
			printf("pos %d right\n",i);
			i--;
			j--;
			continue;
		case 1:
			//printf("(%d,%d) substitute\n",i,j);
			printf("pos %d substitute (%c-->%c)\n",i,a[i-1],b[j-1]);
			i--;
			j--;
			substitution++;
			continue;
		case 2:
			//printf("(%d,%d) insert\n",i,j);
			printf("pos %d insert (%c)\n",i,b[j-1]);
			j--;
			insertion++;
			continue;
		case 3:
			//printf("(%d,%d) delete\n",i,j);
			printf("pos %d delete (%c)\n",i,a[i-1]);
			i--;
			deletion++;
			continue;
		}
	}

	printf("insert:%d,delete:%d,substitute:%d\n",insertion,deletion,substitution);
}

下面是計算HelloWorld和aHelloWored的編輯距離結果顯示,圖中矩陣爲回溯矩陣

系統對算法進行改進後重新定義了編輯操作,不是比較字符串而是比較節點集合,今天就先寫到這,下一篇文章中會將基於樹的編輯距離計算和dom樹遍歷過程以及網頁模板訓練都寫出來供大家參考學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章