/// <summary> /// 移動先の点のベクトルを計算 /// </summary> /// <param name="vMinus">下の斜線上の点</param> /// <param name="vPlus">右の斜線上の点</param> /// <returns>移動先の点</returns> protected static Vector Move(Vector vMinus, Vector vPlus) { V_PARAMETER param = GetVStatus(vMinus, vPlus); Vector result = new Vector(); switch (param) { //vに何も定義されていない場合は左上になります。 case V_PARAMETER.V_INIT: result.Set( 0, 0, new Vector(0, 0, null)); break; //右の斜線上の点を採用する場合。下に移動します。 case V_PARAMETER.V_X: result.Set( vPlus.fX, vPlus.fY + 1, vPlus); break; //下の斜線上の点を採用する場合。右に移動します。 case V_PARAMETER.V_Y: result.Set( vMinus.fX + 1, vMinus.fY, vMinus); break; } return result; }
/// <summary> /// O(ND)アルゴリズムの処理 /// </summary> /// <param name="dest">変更後のセル範囲</param> /// <param name="src">変更前のセル範囲</param> /// <returns>解析結果の最終位置ベクトル</returns> protected static Vector GetEndPoint(Excel.Range dest, Excel.Range src) { //配列vはそれぞれの斜線上の点の位置を表します。 Vector[] v = new Vector[dest.Count + src.Count + 3]; for (int i = 0; i < v.Length; ++i) { v[i] = null; } int offset = src.Count + 1; List<int[]> cellList1 = new List<int[]>(); foreach (Excel.Range cell in dest) { int[] tmp = { cell.Row, cell.Column }; cellList1.Add(tmp); } List<int[]> cellList2 = new List<int[]>(); foreach (Excel.Range cell in src) { int[] tmp = { cell.Row, cell.Column }; cellList2.Add(tmp); } //Dを1ずつ増やしてループします。 for (var d = 0; d <= dest.Count + src.Count; d++) { //斜線kをどこからどこまで調べる必要があるかを決めます。 int k_max = (d <= dest.Count) ? d : (dest.Count - (d - dest.Count)); int k_min = (d <= src.Count) ? d : (src.Count - (d - src.Count)); for (var k = -k_min; k <= k_max; k += 2) { int index = offset + k; //移動先の点を計算 Vector result = Move(v[index - 1], v[index + 1]); //縦と横の文字が同じ場合は斜めに移動できます。移動できるだけ移動します。 while (((result.fX < dest.Count) && (result.fY < src.Count)) && (Convert.ToString(dest[cellList1[result.fX][0], cellList1[result.fX][1]].Value) == Convert.ToString(src[cellList2[result.fY][0], cellList2[result.fY][1]].Value))) { result.fX++; result.fY++; } //配列vにxとyと移動元の点(parent)を格納します。 if (v[index] == null) { v[index] = new Vector(); } v[index].Set(result.fX, result.fY, result.fParent); //右下にたどりついたらその点を返します。 if ((dest.Count <= result.fX) && (src.Count <= result.fY)) { return v[index]; } } } return null; }
/// <summary> /// O(ND)アルゴリズムの処理 /// </summary> /// <param name="dest">変更後の文字列</param> /// <param name="src">変更前の文字列</param> /// <returns>解析結果の最終位置ベクトル</returns> protected static Vector GetEndPoint(string dest, string src) { //配列vはそれぞれの斜線上の点の位置を表します。 Vector[] v = new Vector[dest.Length + src.Length + 3]; for (int i = 0; i < v.Length; ++i) { v[i] = null; } int offset = src.Length + 1; int k_min, k_max, d, k, index; //Dを1ずつ増やしてループします。 for (d = 0; d <= dest.Length + src.Length; d++) { //斜線kをどこからどこまで調べる必要があるかを決めます。 k_max = (d <= dest.Length) ? d : (dest.Length - (d - dest.Length)); k_min = (d <= src.Length) ? d : (src.Length - (d - src.Length)); for (k = -k_min; k <= k_max; k += 2) { index = offset + k; //移動先の点を計算 Vector result = Move(v[index - 1], v[index + 1]); //縦と横の文字が同じ場合は斜めに移動できます。移動できるだけ移動します。 while (((result.fX < dest.Length) && (result.fY < src.Length)) && (dest[result.fX] == src[result.fY])) { result.fX++; result.fY++; } //配列vにxとyと移動元の点(parent)を格納します。 if (v[index] == null) { v[index] = new Vector(); } v[index].Set(result.fX, result.fY, result.fParent); //System.Diagnostics.Debug.WriteLine(index + "," + result.fX + "," + result.fY + "," + result.fParent.fX + "," + result.fParent.fY); //右下にたどりついたらその点を返します。 if ((dest.Length <= result.fX) && (src.Length <= result.fY)) { return v[index]; } } } return null; }
/// <summary> /// O(ND)アルゴリズムによる変更解析結果から変更解析結果文字列を生成 /// </summary> /// <param name="dest">変更後の文字列</param> /// <param name="src">変更前の文字列</param> /// <returns>変更解析結果文字列</returns> public static RevString GetDiffStringOND(string dest, string src) { Vector parent = new Vector(); RevString revStr = new RevString(); int x, y, minLen; //O(ND)アルゴリズムの実行 Vector point = GetEndPoint(dest, src); while (point.fParent != null) { parent = point.fParent; x = point.fX - parent.fX; y = point.fY - parent.fY; minLen = Math.Min(x, y); //変化なし for (var i = 0; i < minLen; i++) { revStr.Push( dest[point.fX - i - 1], REVISION_KIND.SAME); } //追加 if (y < x) { revStr.Push( dest[parent.fX], REVISION_KIND.INSERT); } //削除 else if (x < y) { revStr.Push( src[parent.fY], REVISION_KIND.DELETE); } point = parent; } return revStr; }
/// <summary> /// 斜線kで、下の斜線上の点か、右の斜線上の点か、どちらから移動するかを決める関数です。 /// より右にあるほうを採用します。 /// </summary> /// <param name="vMinus">下の斜線上の点</param> /// <param name="vPlus">右の斜線上の点</param> /// <returns>移動方向を規定する列挙型変数</returns> private static V_PARAMETER GetVStatus(Vector vMinus, Vector vPlus) { //vに何も定義されていない場合 if ((vMinus == null) && (vPlus == null)) { return V_PARAMETER.V_INIT; } //下の斜線上の点がない場合 if (vMinus == null) { return V_PARAMETER.V_X; } //右の斜線上の点がない場合 if (vPlus == null) { return V_PARAMETER.V_Y; } //2つの点の内、より右にあるほうを採用 if (vMinus.fX < vPlus.fX) { return V_PARAMETER.V_X; } else { return V_PARAMETER.V_Y; } }
/// <summary> /// フィールド設定関数 /// </summary> /// <param name="x">fXの設定値</param> /// <param name="y">fYの設定値</param> /// <param name="parent">fParentの設定値</param> public void Set(int x, int y, Vector parent) { fX = x; fY = y; fParent = parent; }
/// <summary> /// コンストラクタ /// </summary> /// <param name="x">fXの初期値</param> /// <param name="y">fYの初期値</param> /// <param name="parent">fParentの初期値</param> public Vector(int x, int y, Vector parent) { Set(x, y, parent); }
/// <summary> /// コンストラクタ(x,yを0に、parentをnullに初期化) /// </summary> public Vector() { fX = 0; fY = 0; fParent = null; }