注意要把string類型的詞轉成unicode32的,這樣計算的長度就是字符的長度
數據整理
如果文本中有英文,如“abc”,把”abc”看成一個詞來計算,返回一個vector<vector<int>>
類型的詞
vector<vector<int>> getRealWord(vector<int> result) {
vector<vector<int>> realWord;
vector<int> temp;
for (auto it = result.begin(); it != result.end(); ++it) {
//判斷是否英文或數字
if ((*(it) > u'\u0061' && *(it) < u'\u007a')
|| (*(it) > u'\u0041' && *(it) < u'\u005a')
|| (*(it) >= u'\u0030' && *(it) <= u'\u0039'))
{
temp.push_back(*it);
}
else {
if (!temp.empty()) {
realWord.push_back(temp);
//清空一下,只記錄一個單詞
temp.clear();
}
temp.push_back(*it);
}
}
realWord.push_back(temp);
return realWord;
}
UTF8轉UTF32
#include "utf8.h"
std::vector<int> UTF8ToUTF32(const char* str) {
std::vector<int> result;
utf8::unchecked::utf8to32(str, str + strlen(str), back_inserter(result));
return result;
}
最大長度
//計算最大長度
int Max(std::string str1, std::string str2) {
//類型轉化 採用unicode編碼
vector<vector<int>> pre = getRealWord(UTF8ToUTF32(str1.c_str()));
vector<vector<int>> next = getRealWord(UTF8ToUTF32(str2.c_str()));
int a = pre.size();
int b = next.size();
return a >b ? a : b;
}
最小長度
int Min(int a,int b){
return a < b ? a : b;
}
字符串是否部分包含
//求兩個vector向量是否有交集
void vectorIntersect(const vector<int>& v1, const vector<int>& v2, vector<int>& des)//求交集
{
int i, j;
i = j = 0;//定位到2個有序向量的頭部
des.clear();
while (i < v1.size() && j < v2.size())
{
if (v1[i] == v2[j]) //相等則爲交集的元素之一
{
des.push_back(v1[i]);
i += 1;
j += 1;
}
else if (v1[i] < v2[j]) //不相等時,指示較小元素額標記加一
{
i += 1;
}
else
{
j += 1;
}
}
}
//判斷字符串是否部分包含另一個字符串
bool SearchPartStr(std::string str1, std::string str2)
{
vector<int> pre = UTF8ToUTF32(str1.c_str());
vector<int> next = UTF8ToUTF32(str2.c_str());
vector<int> result;
vectorIntersect(pre, next, result);
//要除去全包含的情況
if (str1 != str2&&!result.empty()
&& !SearchAllStr(str1.c_str(), str2.c_str())
&& !SearchAllStr(str2.c_str(), str1.c_str()))
{
return true;
}
else
{
return false;
}
}
字符串是否全部包含
bool SearchAllStr(const char *pSource, const char *pSearch)
{
//在pSource查找字符pSearch是否存在
int lenSource = strlen(pSource);
int lenSearch = strlen(pSearch);
int firstCharNum = lenSource + 1;
//判斷搜索字符串是否比源字符串大
if (lenSearch > lenSource)
{
return false;
}
//遍歷字符串 找到第一個相同的字符位置 firstCharNum
for (int i = 0; i < lenSource; i++)
{
if (pSource[i] == pSearch[0])
{
//找到目標字符
firstCharNum = i;
break;
}
}
if (firstCharNum > lenSource || (lenSource - firstCharNum) < lenSearch)
{
//沒有找到相同字符
return false;
}
//首字符之後字符連續相同返回true
int j = 1;
for (int i = firstCharNum + 1; i < (firstCharNum + lenSearch); i++)
{
//遍歷字符串
if (pSource[i] == pSearch[j])
{
//找到目標字符
j++;
}
else
{
//遞歸調用自己
const char *newsource = pSource + firstCharNum + 1;
return SearchAllStr(newsource, pSearch);
}
}
return true;
}
編輯距離
參考文章《編輯距離及編輯距離算法》
編輯距離矩陣
//編輯距離矩陣
int ** GetEditMatrix(std::string str1, std::string str2) {
vector<vector<int>> pre = getRealWord(UTF8ToUTF32(str1.c_str()));
vector<vector<int>> next = getRealWord(UTF8ToUTF32(str2.c_str()));
int max1 = pre.size();
int max2 = next.size();
int **ptr = new int *[max1 + 1];
for (int i = 0; i < max1 + 1; i++)
{
ptr[i] = new int[max2 + 1];
}
for (int i = 0; i < max1 + 1; i++)
{
ptr[i][0] = i;
}
for (int i = 0; i < max2 + 1; i++)
{
ptr[0][i] = i;
}
for (int i = 1; i < max1 + 1; i++)
{
for (int j = 1; j < max2 + 1; j++)
{
int d;
int temp = Min(ptr[i - 1][j] + 1, ptr[i][j - 1] + 1);
if (pre[i - 1] == next[j - 1])
{
d = 0;
}
else
{
d = 1;
}
ptr[i][j] = Min(temp, ptr[i - 1][j - 1] + d);
}
}
return ptr;
}
編輯距離
//編輯距離
double GetEditDistance(std::string str1, std::string str2)
{
vector<vector<int>> pre = getRealWord(UTF8ToUTF32(str1.c_str()));
vector<vector<int>> next = getRealWord(UTF8ToUTF32(str2.c_str()));
int max1 = pre.size();
int max2 = next.size();
int **ptr = GetEditMatrix(str1, str2);
int dis = ptr[max1][max2];
for (int i = 0; i < max1 + 1; i++)
{
delete[] ptr[i];
ptr[i] = NULL;
}
delete[] ptr;
ptr = NULL;
return dis;
}
最大公共子串長度
//兩個字符串之間最長的相同子字符串的長度
int longestCommonSubstring(std::string s1, std::string s2)
{
vector<int> str1 =UTF8ToUTF32(s1.c_str());
vector<int> str2 =UTF8ToUTF32(s2.c_str());
int len1 = str1.size();
int len2 = str2.size();
if (len1 == 0 || len2 == 0)return 0;
//定義二維數組dp[i][j] 代表串1從0~i這段與串2從0~j這段的公共子串的最大值
//賦初值dp[0~len1][0]=0 dp[0][0~len2]=0
vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if (str1[i - 1] == str2[j - 1]) {
//若相等則上層值+1
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
//若不相等則等於交錯值中的最大值
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[len1][len2];
}
對角線改變元素行下標
//記錄對角線上元素值第一次發生改變的元素行下標
int GetFirstChange(std::string str1, std::string str2) {
//類型轉化 採用unicode編碼
vector<vector<int>> pre = getRealWord(UTF8ToUTF32(str1.c_str()));
vector<vector<int>> next = getRealWord(UTF8ToUTF32(str2.c_str()));
int max1 = pre.size();
int max2 = next.size();
int **ptr = GetEditMatrix(str1,str2);
vector<int> result;
for (int i = 0; i < max1 + 1; i++)
{
for (int j = 0; j < max2 + 1; j++)
{
if (i == j&&i != 0 && j != 0) {
if (ptr[i - 1][j - 1] != ptr[i][j]) {
result.push_back(i);
}
}
/*cout << ptr[i][j] << "";*/
}
/*cout << endl;*/
}
for (int i = 0; i < max1 + 1; i++)
{
delete[] ptr[i];
ptr[i] = NULL;
}
delete[] ptr;
if (!result.empty()) {
return result[0];
}
else {
return 0;
}
}
關聯度計算
論文上的公式
優化前方案
優化後方案
代碼實現
//計算關聯度
double GetCorrelation(std::string str1, std::string str2, int flag) {
//optimiLen優化後的編輯距離
double correlation, correlA, correlB, optimiLen;
vector<int> result;
vector<vector<int>> pre = getRealWord(UTF8ToUTF32(str1.c_str()));
vector<vector<int>> next = getRealWord(UTF8ToUTF32(str2.c_str()));
int m = pre.size();
int n = next.size();
switch (flag)
{
case 0:
//字符串全包含編輯距離替換成兩串長度差的平均值
if (str1 != str2 && (SearchAllStr(str1.c_str(), str2.c_str()) || SearchAllStr(str2.c_str(), str1.c_str())))
{
optimiLen = abs(GetEditDistance(str1, str2)- abs((m-n)/2));
}
//字符串部分包含編輯距離替換最小字符串長度減去字符串交集部分長度後取平均值
else if (SearchPartStr(str1.c_str(), str2.c_str(),result) || SearchPartStr(str2.c_str(), str1.c_str(),result))
{
int t = result.size();
optimiLen = abs(GetEditDistance(str1, str2) - abs((Min(m,n)-t) / 2));
}
else {
optimiLen = GetEditDistance(str1, str2);
}
/*cout << editLen << endl;*/
correlA = double(1 - ( optimiLen/ Max(str1, str2)));
correlB = double(longestCommonSubstring(str1, str2) / double( optimiLen + longestCommonSubstring(str1, str2) + double((m - GetFirstChange(str1, str2)) / m)));
/*cout << correlA << "," << correlB << endl;*/
//返回兩種方法的最大值
if ( optimiLen != 0) {
correlation = MaxCorrelation(correlA, correlB);
}
else {
correlation = MaxCorrelation(GetCorrelation(str1,str2,1), GetCorrelation(str1, str2, 2));
}
break;
case 1:
//方法一: 關聯度=1-(編輯距離/詞最大長度)
correlation = double(1 - (GetEditDistance(str1, str2) / Max(str1, str2)));
break;
case 2:
//方法二: 關聯度=最大子串長度/(最大子串長度+編輯距離+(第一個字符串長度-對角線上元素值第一次發生改變的元素行下標)/第一個字符串長度))
correlation = double(longestCommonSubstring(str1, str2) / double(GetEditDistance(str1,str2) + longestCommonSubstring(str1, str2) + double((m - GetFirstChange(str1, str2)) / m)));
break;
case 3:
//方法三: 關聯度=1/(編輯距離+1)
correlation = double(1 / (GetEditDistance(str1, str2) + 1));
break;
default:
break;
}
return correlation;
}